Skip to content
FormSubmit

HTML

How to make an HTML contact form work without a backend

Make a plain HTML contact form send emails without PHP, Node or a server — by pointing it at a hosted form backend. Copy-paste code included.

2 min readUpdated Sep 28, 2026

To make an HTML form work without a backend, set its action attribute to a hosted form endpoint such as https://formsubmit.app/f/YOUR_FORM_ID and its method to POST. The form backend receives the submission, filters spam, stores it and emails it to you. You don't write PHP, Node.js, or any server code, and it works on any static host — GitHub Pages, Netlify, Vercel, Cloudflare Pages, S3 or plain shared hosting.

Why an HTML form can't send email on its own

A <form> element only knows how to package the values of its inputs and send them in an HTTP request to a URL. Something on the other end has to receive that request, do something useful with it and respond. Traditionally that was a PHP script on the same server, which meant configuring mail() or SMTP, fighting deliverability, and hardening the script against spam.

A mailto: action looks like a shortcut but it only opens the visitor's email client (if they have one configured) and gives you no control, no spam protection and no record of the message.

A form backend is a hosted service that plays the role of that server script for you.

Step 1: Create your endpoint

Sign in to FormSubmit with Google and click New form. You'll get an endpoint like:

text
https://formsubmit.app/f/k3m9p2qabx

Step 2: Point your form at it

Here's a complete, accessible contact form. Replace YOUR_FORM_ID with your id:

contact.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
  <div>
    <label for="fs-name">Name</label>
    <input id="fs-name" type="text" name="name" placeholder="Jane Doe" required>
  </div>
  <div>
    <label for="fs-email">Email</label>
    <input id="fs-email" type="email" name="email" placeholder="jane@example.com" required>
  </div>
  <div>
    <label for="fs-message">Message</label>
    <textarea id="fs-message" name="message" rows="5" placeholder="How can we help?" required></textarea>
  </div>
  <!-- Honeypot: leave this hidden field empty to catch bots -->
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <!-- Optional: where to send people after submitting -->
  <!-- <input type="hidden" name="_redirect" value="https://yoursite.com/thanks"> -->
  <button type="submit">Send</button>
</form>

The important parts are:

  • action="https://formsubmit.app/f/YOUR_FORM_ID" — where the data goes.
  • method="POST" — required.
  • A name on every input. Inputs without a name are not submitted by the browser. The name becomes the label in your email ("name" becomes "Name", "company_size" becomes "Company size").
  • The hidden _gotcha input — a honeypot that catches bots. Leave it empty and hidden.

Step 3: Style it

Want something that looks good without writing CSS? Use the version with a small embedded stylesheet:

contact-styled.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST" class="fs-form">
  <div class="fs-field">
    <label for="fs-name" class="fs-label">Name</label>
    <input id="fs-name" type="text" name="name" placeholder="Jane Doe" required class="fs-input">
  </div>
  <div class="fs-field">
    <label for="fs-email" class="fs-label">Email</label>
    <input id="fs-email" type="email" name="email" placeholder="jane@example.com" required class="fs-input">
  </div>
  <div class="fs-field">
    <label for="fs-message" class="fs-label">Message</label>
    <textarea id="fs-message" name="message" rows="5" placeholder="How can we help?" required class="fs-input"></textarea>
  </div>
  <!-- Honeypot: leave this hidden field empty to catch bots -->
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <!-- Optional: where to send people after submitting -->
  <!-- <input type="hidden" name="_redirect" value="https://yoursite.com/thanks"> -->
  <button type="submit" class="fs-button">Send</button>
</form>

<style>
  .fs-form { max-width: 32rem; margin: 0 auto; display: grid; gap: 1.1rem; font-family: system-ui, sans-serif; }
  .fs-field { display: grid; gap: 0.35rem; border: 0; padding: 0; margin: 0; }
  .fs-label { font-size: 0.875rem; font-weight: 600; color: #18181b; }
  .fs-input { width: 100%; box-sizing: border-box; border: 1px solid #d4d4d8; border-radius: 0.5rem; padding: 0.6rem 0.75rem; font: inherit; font-size: 0.9rem; }
  .fs-input:focus { outline: 2px solid #6366f155; border-color: #6366f1; }
  .fs-choice { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; }
  .fs-help { font-size: 0.75rem; color: #71717a; margin: 0; }
  .fs-button { background: #18181b; color: #fff; border: 0; border-radius: 0.5rem; padding: 0.7rem 1rem; font-weight: 600; cursor: pointer; }
  .fs-button:hover { background: #27272a; }
</style>

Using Tailwind? The form generator exports a Tailwind version too.

Step 4: Choose what happens after submit

By default visitors land on a clean hosted thank-you page. To send them to your own page, add a hidden _redirect field or set a redirect URL in the form's settings:

html
<input type="hidden" name="_redirect" value="https://yoursite.com/thanks.html">

Other useful hidden fields are _subject (email subject) and _replyto. See control fields.

Step 5: Test it

Open the page, submit the form, and you should see:

  1. A redirect to the thank-you page.
  2. The submission in your FormSubmit dashboard.
  3. An email in your inbox with every field, where hitting Reply answers the visitor directly.

Optional: stay on the page with a little JavaScript

If you'd rather show "Thanks!" inline, intercept the submit and use fetch. FormSubmit returns JSON when you send Accept: application/json:

form.js
// Works with any existing <form> — add id="contact-form" to it.
const form = document.getElementById("contact-form");
const renderedAt = Date.now();

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const data = new FormData(form);
  data.set("_ts", String(renderedAt));

  const res = await fetch("https://formsubmit.app/f/YOUR_FORM_ID", {
    method: "POST",
    body: data, // or JSON.stringify(Object.fromEntries(data)) with Content-Type: application/json
    headers: { Accept: "application/json" },
  });
  const json = await res.json();

  if (json.ok) {
    form.reset();
    form.insertAdjacentHTML("afterend", "<p>Thanks! We'll be in touch soon.</p>");
  } else {
    alert(json.error?.message ?? "Something went wrong");
  }
});

Hardening checklist

Frequently asked questions

Can an HTML form send an email by itself?

No. HTML can only send the data somewhere. A mailto action opens the visitor's email app, which is unreliable. You need a server — or a hosted form backend — to receive the data and send the email.

Do I need JavaScript?

No. A standard form post works without any JavaScript. JavaScript is only needed if you want to stay on the page and show an inline message.

Is it free?

FormSubmit's free plan includes one form and 50 submissions per month, which is plenty for most personal sites.

Keep reading

Your form backend is 60 seconds away

Sign in with Google, create a form, paste the endpoint. Free forever for small sites — no credit card.