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.
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.
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.
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>
Formigo accepted the submission and forwards it to your Email channel.
Read retry_after from the JSON body and tell the visitor when to retry.
Anything else falls through to a friendly retry message.
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.
Common questions about sending an HTML or JavaScript form straight to email.
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.
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.
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.
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.
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.
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.