Send an HTML contact form to email

Any page that can include a <script> can send its form through FormRoute. This guide uses plain HTML and a few lines of JavaScript, with no build step and no server of your own.

You need a FormRoute account with a verified domain and a form whose fields are name, email and message, or change the names below to match yours. Getting started covers the setup.

1. Add the form

Use your own markup. Each name attribute must match a field you set up in FormRoute; here the fields are name, email and message.

Keep the hidden hp input. It is the honeypot: people never see it, bots fill it in.

contact.html
<form id="contact-form">
  <label>Name <input name="name" required maxlength="120"></label>
  <label>Email <input name="email" type="email" required maxlength="254"></label>
  <label>Message <textarea name="message" required maxlength="5000"></textarea></label>

  <!-- Honeypot: hidden from people, must stay empty -->
  <div aria-hidden="true" style="position:absolute;left:-10000px">
    <label>Leave this empty <input name="hp" tabindex="-1" autocomplete="off"></label>
  </div>

  <button type="submit">Send</button>
  <p id="contact-status" role="status"></p>
</form>

2. Add the script

Place it after the form, and replace fr_your_site_key with the site key from your FormRoute account. The key identifies your form; it is meant to be public.

The script measures how long the form was open (t), sends the fields as JSON and shows the result.

contact.html
<script>
  const ENDPOINT = "https://form-route.com/api/v1/send";
  const SITE_KEY = "fr_your_site_key"; // from your FormRoute account

  const form = document.querySelector("#contact-form");
  const status = document.querySelector("#contact-status");
  const shownAt = Date.now();

  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    const data = new FormData(form);
    const button = form.querySelector("button[type=submit]");
    button.disabled = true;
    status.textContent = "Sending…";

    try {
      const response = await fetch(ENDPOINT, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          siteKey: SITE_KEY,
          t: Date.now() - shownAt,   // ms since the form was shown
          hp: data.get("hp") ?? "",  // honeypot, empty for people
          name: data.get("name"),
          email: data.get("email"),
          message: data.get("message"),
        }),
      });

      if (response.ok) {
        form.reset();
        status.textContent = "Thanks, your message was sent.";
      } else {
        status.textContent = "Sorry, that didn't go through. Please try again.";
      }
    } catch {
      status.textContent = "Sorry, that didn't go through. Please try again.";
    } finally {
      button.disabled = false;
    }
  });
</script>

3. List your website in FormRoute

In your FormRoute account, open your form and add every address the form is served from to Allowed websites, for example https://www.example.com and https://example.com. Each entry is a scheme and host, with a port only if your site uses one.

A browser submission from an address that isn't listed is refused, and your account shows it as Rejected: website not allowed. For local development, use the exact address your dev server prints.

4. Send a test

Open the page, fill in the form and send it. The submission appears on your FormRoute home page within seconds, as Sent or with the reason it was refused, and the email arrives from your own domain.

Fill the form in at a human pace: a submission sent within 1.2 seconds of the form appearing is treated as automated.

Good to know

  • Testing locally? Add your local address too, such as http://localhost:8080, and remove it when you go live.
  • Styling is yours: FormRoute never changes the form's markup or look.

More guides

Next.js · React · Vue · Astro · WordPress · Webflow · Framer · Hugo, Jekyll and Eleventy