← Guides

How to Add a Contact Form to a Static Site

How to Add a Contact Form to a Static Site

You built a static site — a portfolio, a landing page, a side project — and now you need a contact form. Name, email, message, submit. Should take five minutes.

Except static sites don’t have a backend. There’s no server to receive the form data, no database to store it, no way to send yourself a notification when someone fills it out. Your HTML can render the form, but when a visitor clicks “Send,” the data has nowhere to go.

This is the most common gap in static site hosting. Here’s how to fill it.

Why Static Sites Can’t Handle Forms Alone

A contact form needs two things: a frontend (the HTML form the visitor sees) and a backend (something that receives the POST request, stores the data, and optionally sends you an email).

Static sites only have the frontend. When you deploy HTML, CSS, and JavaScript to a CDN, there’s no server processing requests. A form with method="POST" needs a URL to submit to — and that URL needs to point to something that can actually handle the data.

This means every static site with a contact form needs an external service to act as the backend. The question is which one.

Option 1: Third-Party Form Services

Services like Formspree, Getform, and Basin provide hosted form endpoints. You point your form’s action attribute to their URL, and they handle the rest. (See our form backend comparisons for a detailed breakdown of each service.)

<form action="https://formspree.io/f/your-form-id" method="POST">
  <input type="text" name="name" placeholder="Name" required>
  <input type="email" name="email" placeholder="Email" required>
  <textarea name="message" placeholder="Message" required></textarea>
  <button type="submit">Send</button>
</form>

This works. No backend code, no JavaScript required. The visitor fills out the form, clicks submit, and the data goes to Formspree’s servers.

The tradeoffs:

  • Another account to manage. You sign up for the service, configure your form, and manage submissions in their dashboard — separate from your hosting.
  • Free tiers are limited. Formspree’s free plan allows 50 submissions per month. Getform gives you 50. Need more? Plans start at $8-10/month.
  • Your form data lives on someone else’s servers. You’re trusting a third party with your visitors’ contact information.
  • Branding on free tiers. Some services add their logo or a redirect page on free plans.

For a personal site with occasional contact requests, this is a reasonable option. But you’re adding a service to your stack — another login, another dashboard, another monthly bill if you outgrow the free tier.

Option 2: Build Your Own Endpoint

If you want full control, you can build a serverless function to handle submissions. Here’s a minimal example with a Cloudflare Worker:

export default {
  async fetch(request) {
    if (request.method !== "POST") {
      return new Response("Method not allowed", { status: 405 });
    }

    const data = await request.formData();
    const name = data.get("name");
    const email = data.get("email");
    const message = data.get("message");

    // Store in a database, send an email, etc.
    console.log({ name, email, message });

    return Response.redirect("https://yoursite.com/thanks", 303);
  },
};

Then point your HTML form at the deployed endpoint:

<form action="https://your-worker.your-subdomain.workers.dev" method="POST">
  <input type="text" name="name" placeholder="Name" required>
  <input type="email" name="email" placeholder="Email" required>
  <textarea name="message" placeholder="Message" required></textarea>
  <button type="submit">Send</button>
</form>

The tradeoffs:

  • You build everything yourself. Spam protection, rate limiting, email notifications, data storage — all your responsibility.
  • CORS configuration. If you submit via JavaScript (fetch), you need to handle cross-origin headers. HTML form submissions avoid this, but then you lose control over the redirect experience.
  • Two things to deploy. Your static site and your form endpoint are separate deployments, possibly on separate platforms.
  • Maintenance overhead. When something breaks at 2 AM, it’s your code to debug.

This is the right choice if you need custom logic — validation rules, integrations with other services, or data processing before storage. But for a standard contact form, it’s a lot of infrastructure for a simple problem.

Option 3: Use a Hosting Platform with Built-In Forms

Some hosting platforms include form handling as part of the service, so there’s nothing extra to sign up for or deploy.

On ZeroDeploy, you point your form’s action to /_forms/<name> and it works:

<form action="/_forms/contact" method="POST">
  <input type="text" name="name" placeholder="Name" required>
  <input type="email" name="email" placeholder="Email" required>
  <textarea name="message" placeholder="Message" required></textarea>
  <button type="submit">Send</button>
</form>

No API keys, no third-party accounts, no serverless functions. The form is auto-created on the first submission. After that, you can view submissions in the dashboard, export as CSV, or set up email notifications.

Want to submit via JavaScript instead? Send JSON:

const data = Object.fromEntries(new FormData(form));

const response = await fetch("/_forms/contact", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify(data),
});

const result = await response.json();
if (result.success) {
  // Show success message
}

Both approaches include built-in spam prevention (honeypot field) and rate limiting. No configuration needed.

What you get out of the box:

  • Form submissions stored and viewable in your dashboard
  • CSV export for all submissions
  • Email notifications when new submissions come in (Pro plan)
  • Rate limiting (10 submissions/minute per IP)
  • Honeypot spam protection
  • Works with plain HTML forms and JavaScript fetch

The contact form is part of your hosting — not a separate vendor. If you’re specifically dealing with Netlify Forms breaking in React, we cover that in a separate deep dive.

Adding Spam Protection

Whichever option you choose, you’ll want basic spam protection. The simplest approach is a honeypot field — a hidden input that real users never see but bots fill in automatically:

<form action="/_forms/contact" method="POST">
  <!-- Honeypot: hidden from humans, filled by bots -->
  <input type="text" name="_hp" style="display:none" tabindex="-1" autocomplete="off">

  <input type="text" name="name" placeholder="Name" required>
  <input type="email" name="email" placeholder="Email" required>
  <textarea name="message" placeholder="Message" required></textarea>
  <button type="submit">Send</button>
</form>

If the _hp field has a value, the submission is likely from a bot. ZeroDeploy automatically rejects submissions with a filled honeypot field. If you’re using a third-party service, check their docs for supported spam prevention methods — most offer honeypot or reCAPTCHA integration.

Showing Success and Error Messages

After a visitor submits your contact form, they should see feedback. With a standard HTML form, the simplest approach uses query parameters after redirect:

<p id="form-message"></p>

<script>
  const params = new URLSearchParams(window.location.search);
  if (params.get("success") === "true") {
    document.getElementById("form-message").textContent =
      "Thanks! We'll be in touch.";
  }
  if (params.get("error")) {
    document.getElementById("form-message").textContent =
      "Something went wrong. Please try again.";
  }
</script>

ZeroDeploy redirects back to the form page with ?success=true or ?error=<reason> after each submission, so this pattern works out of the box.

If you submit via JavaScript, you have full control over the UI — show a success message, hide the form, or redirect to a thank-you page.

Which Option Should You Pick?

Third-Party ServiceCustom EndpointBuilt-In (ZeroDeploy)
Setup time5-10 minutes1-2 hours2 minutes
Separate accountYesNo (but separate deploy)No
Free tier50 submissions/moDepends on platform100 submissions/mo
Spam protectionVariesBuild your ownBuilt-in honeypot
Email notificationsVaries by planBuild your ownIncluded (Pro)
Custom logicLimitedFull controlStandard forms
MaintenanceLowHighNone

Use a third-party service if you’re already hosting on a platform without built-in forms (like GitHub Pages) and don’t want to write backend code.

Build your own endpoint if you need custom validation, integrations, or processing logic that no off-the-shelf service supports.

Use built-in form handling if you want forms to be part of your hosting with zero extra setup. Deploy your site, add a form, done.

Frequently Asked Questions

Can you add a contact form to a static site without a backend?

Yes. Static sites can handle forms by posting submissions to an external endpoint — either a third-party service like Formspree, a serverless function you build yourself, or a hosting platform with built-in form handling like ZeroDeploy.

What is the easiest way to add a contact form to a static website?

The easiest way is to use a hosting platform that includes form handling. On ZeroDeploy, you set your form’s action attribute to /_forms/contact and method to POST. No signup for a separate service, no API keys, no JavaScript required.

Do static site contact forms work without JavaScript?

Yes. A standard HTML form with an action attribute pointing to a form endpoint works without any JavaScript. The browser submits the form data as a POST request, and the endpoint handles storage and redirection. JavaScript is only needed if you want to submit without a page reload.

Wrap Up

Adding a contact form to your website is a solved problem — but the solutions have different tradeoffs. Third-party services work but add another vendor. Custom endpoints give you control but add maintenance. Built-in form handling keeps everything in one place.

If you’re looking for the path with the least friction, ZeroDeploy’s built-in forms are included on every plan — deploy your site, add a form tag, and start collecting submissions. No extra services, no extra billing.

Get started with ZeroDeploy or read the quickstart guide to deploy your first site in under 5 minutes.