Documentation / Code Examples / Submit a Form to Email (HTML/JS)
HTML & JavaScript

Submit a Form to Email with Plain HTML & JavaScript

The retro way: no framework, no server, no nonsense. Point an HTML form at Formigo and your submissions land in your inbox. Want it slicker? Add a few lines of vanilla JavaScript for inline success and error messages.

How "form to email" works with Formigo

You don't write any backend code. You POST your form to a Formigo endpoint that looks like https://formigo.io/f/your-form, and Formigo delivers each submission to your configured channels. To send a form to email without a backend, connect an Email channel to your form and every submission lands in your inbox - no SMTP, no PHP mail(), no spam-folder gymnastics.

Pure HTML form (no JavaScript)

The simplest HTML form to email

Just an HTML <form> - works in any page, anywhere

This is the whole trick: a standard <form> with action pointing at your Formigo endpoint and method="POST". No JavaScript, no backend. The browser does a full-page submit, Formigo accepts it, and your Email channel forwards it to your inbox. Copy this, change your-form to your form's slug, and you have an HTML form that sends to email.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Contact Us</title>
</head>
<body>
  <h1>Contact Us</h1>

  <!-- Submit this form straight to email via Formigo -->
  <form action="https://formigo.io/f/your-form" method="POST">
    <label for="name">Name</label>
    <input type="text" id="name" name="name" required>

    <label for="email">Email</label>
    <input type="email" id="email" name="email" required>

    <label for="message">Message</label>
    <textarea id="message" name="message" rows="5" required></textarea>

    <!-- Spam protection: leave the honeypot empty; bots fill it in -->
    <input type="text" name="_formigo_hp" value=""
           style="display:none" tabindex="-1" autocomplete="off">
    <input type="hidden" name="_formigo_t" value="">

    <button type="submit">Send Message</button>
  </form>

  <!-- One tiny line to timestamp the submission (optional but recommended) -->
  <script>
    document.querySelector('input[name="_formigo_t"]').value =
      Math.floor(Date.now() / 1000);
  </script>
</body>
</html>

What happens on submit? The browser POSTs the form fields to https://formigo.io/f/your-form and Formigo redirects back to the page the visitor submitted from. Because you connected an Email channel, the submission is forwarded to your email address. That's a complete "send form to email" flow with zero backend code - though the visitor sees a page reload rather than a success message. Add the fetch version below if you want to keep them on the page.

Progressive JavaScript (fetch)

JavaScript form submission with fetch

No dependencies - pure ES6+ with inline success, error, and rate-limit handling

Want to keep visitors on the page and show a friendly message instead of a full reload? Enhance the same form with a little vanilla JavaScript. We intercept the submit, send the data with fetch, and handle three outcomes: success (2xx), a rate-limit response (429 with a retry_after hint), and everything else. Formigo accepts both form-encoded and JSON and is CORS-enabled, so this works straight from the browser. If JavaScript fails to load, the plain HTML form above still submits to email - true progressive enhancement.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Contact Us</title>
  <style>
    .message { padding: 1rem; margin-top: 1rem; border-radius: 8px; }
    .success { background: #d4edda; color: #155724; }
    .error { background: #f8d7da; color: #721c24; }
  </style>
</head>
<body>
  <h1>Contact Us</h1>

  <!-- Same endpoint as the no-JS version; fetch enhances it -->
  <form id="contact-form" action="https://formigo.io/f/your-form" method="POST">
    <label for="name">Name</label>
    <input type="text" id="name" name="name" required>

    <label for="email">Email</label>
    <input type="email" id="email" name="email" required>

    <label for="message">Message</label>
    <textarea id="message" name="message" rows="5" required></textarea>

    <!-- Spam protection: honeypot stays empty, timestamp set on load -->
    <input type="text" name="_formigo_hp" value=""
           style="display:none" tabindex="-1" autocomplete="off">
    <input type="hidden" name="_formigo_t">

    <button type="submit">Send Message</button>
  </form>

  <div id="status"></div>

  <script>
    const form = document.getElementById("contact-form");
    const status = document.getElementById("status");

    // Stamp the submission with the current time when the page loads
    form.elements._formigo_t.value = Math.floor(Date.now() / 1000);

    form.addEventListener("submit", async (e) => {
      e.preventDefault();
      const submitBtn = form.querySelector("button[type=submit]");

      // Collect fields (honeypot + timestamp included automatically)
      const data = Object.fromEntries(new FormData(form));

      submitBtn.disabled = true;
      submitBtn.textContent = "Sending...";
      status.className = "";
      status.textContent = "";

      try {
        const response = await fetch("https://formigo.io/f/your-form", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(data)
        });

        if (response.ok) {
          // 2xx: Formigo accepted it and emails your inbox
          status.className = "message success";
          status.textContent = "Thanks! Your message is on its way to our inbox.";
          form.reset();
          form.elements._formigo_t.value = Math.floor(Date.now() / 1000);
        } else if (response.status === 429) {
          // Rate limited: respect the retry_after hint
          const { retry_after } = await response.json();
          status.className = "message error";
          status.textContent = `Whoa, too fast! Try again in ${retry_after} seconds.`;
        } else {
          throw new Error(`Unexpected status ${response.status}`);
        }
      } catch (error) {
        status.className = "message error";
        status.textContent = "Something went wrong. Please try again.";
        console.error("Submit failed:", error);
      } finally {
        submitBtn.disabled = false;
        submitBtn.textContent = "Send Message";
      }
    });
  </script>
</body>
</html>
Success (2xx)

Formigo accepted the submission and forwards it to your Email channel.

Rate limited (429)

Read retry_after from the JSON body and tell the visitor when to retry.

Error

Anything else falls through to a friendly retry message.

About the spam fields

Two hidden fields keep your inbox clean:

  • _formigo_hp - a hidden honeypot. Humans never see it, so it stays empty; bots tend to fill every field, which flags them.
  • _formigo_t - a timestamp set when the page loads. Submissions that arrive impossibly fast look automated.

Both are optional, but recommended. Add them once and Formigo handles the rest, so your "form to email" flow doesn't turn into a "spam to email" flow.

FAQ

Common questions about sending an HTML or JavaScript form straight to email.

Can I submit an HTML form to email without a backend?

Yes. Point the form's action at your Formigo endpoint and set method="POST". The browser submits it natively, Formigo stores the submission, and your connected Email channel forwards it to your inbox. No server, no server-side language, no hosting requirements beyond serving the HTML file.

Do I need JavaScript to submit a form to email?

No. A plain HTML form works on its own. JavaScript buys you two things: the timestamp spam check, which is one line that stamps _formigo_t on page load, and the ability to show a success message without a page reload by submitting with fetch instead.

What does the visitor see after a plain HTML form submit?

A full page load. Formigo answers a native form POST with a redirect back to the page the form was submitted from, so there is no built-in confirmation screen. If you want the visitor to stay on the page and read a thank-you message, use the fetch version, which intercepts the submit and handles the response in place.

Should I send JSON or form-encoded data?

Either works. A native HTML form sends form-encoded data automatically. With fetch you can send JSON with a Content-Type of application/json, or form-encoded by passing new URLSearchParams(new FormData(form)) as the body and omitting the Content-Type header.

What happens if someone submits the form too many times?

Formigo allows 5 submissions per IP per form every 10 minutes and 20 submissions per IP across all forms per hour. Going over either limit returns HTTP 429 with a JSON body containing retry_after, the number of seconds until the limit resets, so you can tell the visitor exactly when to try again.

Ready to submit your form to email?

Create a form, connect an Email channel, and paste the snippet above. Submissions land in your inbox in minutes - free forever, no credit card, no backend required.