Handle Form Submissions Without a Backend
A practical guide to collecting form data from static sites, JAMstack apps, and single-page applications — no server required.
Handle Form Submissions Without a Backend
Static sites are fast, cheap to host, and dead simple to deploy. But they have one notorious weak spot: forms. With no server-side code running, how do you receive a contact form submission?
This post covers the options, their trade-offs, and the simplest path to production-ready form handling.
Option 1: HTML Form to a Form Service
The cleanest approach for most sites. Point your form's action attribute at a service URL — the service handles receiving, storing, and notifying you.
1<form action="https://formboost.app/f/YOUR_ID" method="POST">
2 <input type="text" name="name" placeholder="Your name" required />
3 <input type="email" name="email" required />
4 <textarea name="message" placeholder="Your message"></textarea>
5 <button type="submit">Send</button>
6</form>Pros: Zero JavaScript, works everywhere, handles spam, sends notifications. Cons: Redirects to a thank-you page on submit (configurable).
Option 2: Fetch API (AJAX Submit)
If you want to stay on the page after submission, use fetch to post the data:
1async function handleSubmit(event) {
2 event.preventDefault();
3
4 const form = event.target;
5 const data = Object.fromEntries(new FormData(form));
6
7 try {
8 const res = await fetch("https://formboost.app/f/YOUR_ID", {
9 method: "POST",
10 headers: { "Content-Type": "application/json" },
11 body: JSON.stringify(data),
12 });
13
14 if (res.ok) {
15 form.reset();
16 showSuccess("Message sent!");
17 } else {
18 throw new Error("Submission failed");
19 }
20 } catch {
21 showError("Something went wrong. Please try again.");
22 }
23}
24
25document.querySelector("form").addEventListener("submit", handleSubmit);Pros: Full control over UX, no page redirect, easy to add loading states.
Option 3: React (with react-hook-form)
For React apps, integrating with a form service is just as simple:
1import { useForm } from "react-hook-form";
2
3export function ContactForm() {
4 const { register, handleSubmit, reset, formState: { isSubmitting } } = useForm();
5
6 const onSubmit = async (data) => {
7 await fetch("https://formboost.app/f/YOUR_ID", {
8 method: "POST",
9 headers: { "Content-Type": "application/json" },
10 body: JSON.stringify(data),
11 });
12 reset();
13 };
14
15 return (
16 <form onSubmit={handleSubmit(onSubmit)}>
17 <input {...register("email", { required: true })} type="email" />
18 <textarea {...register("message")} />
19 <button type="submit" disabled={isSubmitting}>
20 {isSubmitting ? "Sending..." : "Send"}
21 </button>
22 </form>
23 );
24}Spam Protection
Any public form endpoint will attract bots. A few layers of defense:
1. Honeypot fields
A hidden field that only bots fill in:
1<input
2 type="text"
3 name="_honey"
4 style="display:none"
5 tabindex="-1"
6 autocomplete="off"
7/>2. AI filtering
Formboost runs every submission through an AI classifier trained on millions of spam examples. You get a spam score with each submission and automatic filtering in strict mode.
3. Rate limiting
Formboost enforces per-endpoint rate limits so one abuser can't flood your inbox.
Choosing a Service
| Feature | Formboost | DIY serverless |
|---|---|---|
| Setup time | < 1 minute | Hours |
| Spam protection | AI-powered | You build it |
| Email notifications | Built-in | Configure SES/SendGrid |
| Dashboard | Yes | No |
| Cost | Free tier available | Hosting + services |
For most projects, a dedicated form service pays off quickly. The free tier on most services handles personal sites indefinitely, and paid tiers are far cheaper than the engineering time to build it yourself.
Summary
- Use a plain
<form>withaction=for maximum simplicity - Use
fetchor a React hook when you need to control the post-submit UX - Add honeypot fields and use a service with AI filtering to block spam
- Pick a service with a generous free tier so you can start without commitment