Half of all broken forms die from the same bug: inputs without a name attribute. The other half fail for human reasons — too many required fields, unclear labels, no feedback on Submit. Here's the short path to a form that works and that people finish.
<form action="/subscribe" method="POST"> <label for="email">Email</label> <input type="email" id="email" name="email" required> <button type="submit">Subscribe</button> </form>
That's a complete, accessible, validating form. The label's for matches the input's id, which clicks the field when the label is clicked and tells screen readers what they're looking at. The email type gives you format validation and the mobile @-keyboard. required blocks empty submissions. Zero JavaScript so far.
The id is for the page: label linking, CSS, JavaScript hooks. The name is for the data: it's the key your server receives. <input type="email" id="email"> with no name submits nothing at all, silently. Radio buttons group by sharing one name — give each option its own name and users can select every option at once.
| Field | Type | What you get for free |
|---|---|---|
email | Format check, @ keyboard on phones | |
| Age, quantity | number | Numeric entry, min/max/step attributes |
| Phone | tel | Numeric keypad on mobile |
| Appointment | date | Native picker; no 31/31 typos |
| Website | url | URL format enforcement |
| Long answers | textarea | Multi-line input, resizable |
The pattern generalizes: every time you reach for type="text", ask whether something more specific exists. The browser has been shipping these validators for over a decade.
GET puts the values in the URL, which makes sense for search and filter boxes and nothing else — URLs land in server logs, history, and analytics. POST sends values in the request body and is the right call for anything that signs up, contacts, pays, or contains a person's details. When in doubt, POST.
Add fields, set labels and options, and copy clean semantic HTML or JSON — built exactly to the rules above.
Open the Form Builder →Anywhere you point action: your own backend route, a serverless function, or a form service if you don't want to run a server. Three honest options, ranked by effort:
Whatever you choose, add server-side validation and a spam check. HTML attributes are convenience for users, not security for you.
Submit the form empty (browser messages should appear), submit it filled (data should arrive with the right keys), and submit it from your phone (keyboard, zoom, spacing). Validate the received JSON with the JSON formatter to see exactly what your server sees, and if the form is part of a page you're shipping, a quick pass through the schema generator for the surrounding markup keeps the whole page tidy.
HTML can't email anything by itself, and mailto: actions break on most clients and leak the address to scrapers. Use a form backend: a service endpoint, a serverless function, or your own route, then send the email from the server where you can rate-limit and spam-filter it.
Almost always a missing or duplicated name attribute on the inputs. The browser only submits fields that carry a name, and radio buttons only group when they share one. The id is for labels and accessibility; the name is for the data. Print your request body and check which fields arrived.
Use required plus the correct input type: email, number, url, date. The browser blocks submission, shows its own messages, and even supplies the right mobile keyboard. Add pattern for formats like ZIP codes and minlength/maxlength for length. JavaScript validation is then a progressive enhancement for the fancy cases.
POST for anything that creates, changes, or contains personal data, which is nearly every real form. GET appends values to the URL, which suits search and filter boxes and nothing else: URLs end up in logs, browser history, and analytics, and nobody wants a user's password or phone number there.