How to Receive HTML Form Submissions
How to Receive HTML Form Submissions
You’ve built the form. The inputs are labeled, the validation attributes are in place, and the submit button looks right. But when a visitor clicks “Submit,” where does that data actually go?
On a static site, the honest answer is: nowhere — unless you’ve set up something on the other end of your form’s action URL to receive it. The HTML collects the data. The backend is what makes it arrive somewhere useful.
This guide walks through exactly how that works, what your options are, and how to pick the right approach for your project.
The Problem: HTML Forms Need a Destination
An HTML <form> element collects user input. But collecting isn’t the same as receiving. Without a backend to process the submission, the browser has nowhere to send the data — it just sits in the markup, doing nothing.
This is the gap that trips up a lot of people building static sites. You can write a perfect form in HTML, but the moment you need that data — as an email, in a spreadsheet, or in a dashboard — you need something behind the action URL.
Anatomy of a Working HTML Form
Here’s a simple contact form that follows MDN’s form element reference:
<form action="https://your-backend.example/submit" method="POST">
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
</div>
<div>
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<div>
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit">Send Message</button>
</form>
Two attributes do the heavy lifting:
action— The URL where the form data is sent. This is your backend endpoint.method—POSTsends data in the request body (standard for form submissions).GETappends it to the URL as query parameters, which isn’t appropriate for most forms.
The name attribute on each input is what gives the data structure — the backend receives name, email, and message as key-value pairs.
Built-In Browser Validation
HTML5 attributes like required, type="email", minlength, and pattern give you client-side validation for free. According to MDN’s Constraint Validation guide, these semantic attributes handle common validation patterns without JavaScript:
required— Field must have a valuetype="email"— Value must be a valid email formattype="url"— Value must be a valid URLminlength/maxlength— Enforce character limitspattern— Match a regular expression
You can style valid and invalid fields with CSS pseudo-classes:
input:invalid {
border-color: #e53e3e;
}
input:valid {
border-color: #38a169;
}
Client-side validation is a convenience, not security. Always validate on the server side — users can bypass HTML constraints through browser developer tools.
What Happens When Someone Clicks Submit
Here’s the step-by-step:
- User fills in the form fields
- User clicks the submit button
- Browser checks HTML5 validation constraints — if any fail, submission is blocked
- Browser serializes the field data as key-value pairs
- Browser sends an HTTP POST request to the
actionURL - Something at that URL receives the request and processes it
That “something” is the backend. On a static site, there’s no server running PHP, Node.js, or Python to handle step 6. You need an external service, a hosting feature, or a serverless function to fill that role.
How to Receive Submissions: Three Practical Paths
There are three common ways to receive form submissions on a static site. Each trades off complexity, flexibility, and hosting requirements differently.
Path 1: Hosted Form Backend Service
A form backend service gives you a URL endpoint. You point your form’s action at it, and the service handles the core work: receiving the POST request, validating data, filtering spam, and delivering the submission to you.
The setup pattern is the same regardless of which service you choose:
- Create an account with a form backend provider
- Get a URL endpoint
- Set your form’s
actionto that URL - Test a submission
Your HTML stays the same — you only change the action URL. The service handles the infrastructure.
<form action="https://api.firaform.com/f/your-form-id" method="POST">
<!-- same fields as before -->
</form>
This is the lowest-friction option. No code to deploy, no server to manage. The trade-off is that you’re relying on a third-party service for delivery.
Path 2: Custom Backend Code
If your hosting server supports a server-side language — Node.js, Python, PHP, Go, or Ruby — you can write your own form handler. You create a script that listens for POST requests at a URL, parses the form fields, and does whatever you need: send an email, write to a database, forward to an API.
Here’s the pattern in Node.js with Express:
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.post('/contact', (req, res) => {
const { name, email, message } = req.body;
// Validate, filter spam, send email, store in DB, etc.
// This is all your responsibility to implement.
res.send('Thank you! Your message has been sent.');
});
You get full control over the logic. But you’re also fully responsible for validation, spam protection, email delivery, rate limiting, and keeping the server secure and updated. If your hosting provider doesn’t offer a server-side runtime, or you don’t want the maintenance overhead, this path isn’t the right fit.
Path 3: Your Own Server or Serverless Route
If you want full control but Path 2 feels like too much infrastructure, you can deploy a dedicated endpoint without managing a full server. This could be a serverless function (Cloudflare Workers, AWS Lambda, Vercel Functions), a lightweight VPS, or a containerized service.
You write the logic — validation, email delivery, database writes — and deploy it however you want. Here’s a minimal example using a Cloudflare Worker:
export default {
async fetch(request) {
const formData = await request.formData();
const name = formData.get('name');
const email = formData.get('email');
const message = formData.get('message');
// Replace with your email provider call (Resend, Mailgun, SES, etc.)
// or store in D1, call an API, etc.
await sendEmail({ to: '[email protected]', name, email, message });
return new Response('Thank you!', { status: 200 });
}
};
This approach requires deploying and maintaining server-side code. You get complete control, but you’re responsible for validation, spam filtering, and delivery.
Form Backend Services to Consider
If you go with Path 1 (a hosted form backend), here are the services worth evaluating. Each handles the same core problem — receiving form submissions on a static site — but they emphasize different things.
FiraForm
FiraForm is a headless form backend built for static sites. You point your form’s action to a FiraForm endpoint, and it handles submission processing, spam filtering, email notifications, and webhooks. Fields are auto-detected from the first submission — no dashboard configuration needed. Pricing is among the most generous and affordable in the space, and every plan includes file upload support.
Good for: Static sites that need email notifications, a submission dashboard with CSV export, and webhook integrations without per-form pricing. The field auto-detection means you can add, remove, or rename fields in your HTML without touching the backend settings. Built-in CAPTCHA and honeypot spam protection is included on all plans.
Limitations: No built-in payment processing. If you need to accept payments through your form, pair it with a payment service or use serverless functions.
Formspree
Formspree is one of the most widely used form backend services. The core idea is simple: create a form endpoint, point your action at it, and submissions arrive in your inbox. It’s lightweight, requires almost no configuration, and has a free tier with limited submissions.
Good for: Simple contact forms where you just need email delivery. If your form is “name, email, message, send” and you don’t need a dashboard or webhooks, Formspree is straightforward.
Limitations: The free tier is limited, and the dashboard is minimal compared to services that focus on submission management. If you need webhooks or advanced filtering, you’ll want to look at paid tiers or other options.
Netlify Forms
Netlify Forms is built directly into Netlify’s hosting platform. If your site is already deployed on Netlify, forms work out of the box — just add a netlify attribute to your <form> tag and Netlify detects and handles submissions automatically. Spam filtering is included.
Good for: Sites already hosted on Netlify. The setup is genuinely zero-effort if you’re in that ecosystem. No external account, no endpoint URL to manage — it works with your existing deployment.
Limitations: Locked to Netlify hosting. If you move your site to Cloudflare Pages or Vercel, your forms stop working. You’re trading portability for convenience.
Basin
Basin is a form backend with a focus on dashboard management and integrations. It stores submissions in a web dashboard, supports webhooks, and has a free tier. It’s a middle ground between Formspree’s simplicity and more feature-heavy services.
Good for: Teams or projects where you need to view, search, and manage submissions in a dashboard rather than just receiving emails. The webhook support is useful if you want to pipe data to Slack, Zapier, or a custom endpoint.
Limitations: Smaller ecosystem and community than Formspree. The free tier has submission limits that may require an upgrade sooner than expected.
Forminit / Getform
Forminit, formerly Getform.io, provides form endpoints with email forwarding and a simple dashboard. It’s straightforward: point your form, receive submissions via email or in their dashboard.
Good for: Basic endpoint + email needs. If you want something between Formspree’s minimalism and Basin’s dashboard-heavy approach, Forminit is worth a look.
Limitations: Fewer advanced features (webhooks, integrations) compared to Basin or FiraForm. The feature set is more focused on the basics.
How to Choose
| Priority | Consider |
|---|---|
| Dashboard + integrations + best value | FiraForm |
| Simplicity, just email | Formspree |
| Already on Netlify | Netlify Forms |
| Dashboard + integrations | Basin |
| Basic endpoint + email | Forminit |
| Custom server logic | Serverless functions (not a form backend) |
Most of these have free tiers or trials. The fastest way to evaluate them is to build your actual form, point it at two or three services, and see which workflow fits your project.
Choosing the Right Approach
When deciding how to receive form submissions, the factors that matter in practice are:
- Hosting setup — If you’re on Netlify, Netlify Forms is the lowest-friction option. If you’re on Cloudflare Pages, Vercel, or another host, you’ll need a form backend service or serverless function.
- Complexity you need — A simple contact form might only need email delivery. A lead generation workflow might need webhooks to Slack and a searchable dashboard.
- Submission volume — Most services start free but scale differently. Check what happens at 100, 500, and 1,000 submissions.
- Data ownership — Can you export your data easily? CSV export is standard. Check whether you can also access data via API or webhooks.
- Spam protection — Open form endpoints attract bots immediately. Built-in spam protection saves you from writing filtering logic yourself.
The fastest way to find the right fit is to build your actual form, point it at two or three services, and test with real fields and your real hosting setup.
Conclusion
The gap between an HTML form and actual submissions is smaller than it looks. Your HTML stays the same — the key decision is what sits behind the action URL.
The approach is straightforward:
- Build your form with standard HTML elements and named fields
- Point the
actionattribute at a backend — a form service, Netlify Forms, or a serverless function - Add client-side validation with HTML5 constraint attributes
- Choose a delivery method: email, dashboard, webhooks, or all three
Your static site stays fast and simple. The backend handles the submission processing.
If you’re evaluating options, FiraForm, Formspree, Netlify Forms, Basin, and Forminit are all worth comparing. The right choice depends on your hosting setup, submission volume, and which integrations matter to you.
Need Forms For Your Static Site?
FiraForm is the headless form backend built specifically for static sites. No backend code needed—just point your forms to our endpoint and we'll handle submissions, validation, notifications, and more.
Free tier available • No credit card required