The Turnstile: Forms, and Where Products Lose People
Everything a product promises gets cashed in at a form. It's also the one place where a small mistake makes a person give up entirely — and the platform already solved most of it for free.
Twelve fields. You fill them all in, hit Submit, and the page reloads: a red line at the top says "invalid input," the password field is empty, the date you picked is gone, and nothing tells you which field was wrong.
You know this feeling exactly, from the receiving end. What's worth noticing is that everything in it was a decision someone made — including the decision to clear the fields.
Forms are where products lose people. Signing up, paying, booking, searching, writing — every valuable thing a user does passes through one. And it's the part of the interface where the browser has already done most of the work, if you let it.
Use the form element
The most common modern mistake is a set of divs with an onclick handler that calls an API. It works in the demo and quietly loses a pile of behaviour that came free with a real form.
A real form submits when you press Enter in a text field — which is how a large fraction of people submit forms, and they'll never report that it's missing, they'll just think your site is broken. It lets the browser offer to save and later autofill the credentials. It gives you one submit event instead of scattered handlers. It works when JavaScript hasn't loaded or has crashed — which is not a theoretical state; it's every flaky connection and every ad blocker having a bad day.
The same argument as the semantics article, applied to the highest-stakes part of the page: real elements bring behaviour you would otherwise write worse.
Every field is a label and a type
Two attributes carry most of the quality of a form.
A label, always, tied to the field. Not a placeholder. A placeholder disappears the moment someone starts typing — precisely when they most need to recall what was being asked — it fails at low contrast, and it makes a filled field indistinguishable from an empty one at a glance. Label above the field, connected by for, so that clicking the label focuses the field and a screen reader announces the question.
The right type. This is where a laptop-built form goes wrong on a phone. type="email" gives a keyboard with an @ key; type="tel" gives a number pad; type="url", type="date", type="number" all change what the person's thumbs get. The wrong type is a small tax charged to every mobile user, every time.
Then add autocomplete. It's the most under-used attribute in frontend: autocomplete="email", "given-name", "street-address", "cc-number", "one-time-code". Correct values let the browser fill an address in one tap and paste an SMS code without leaving the app. Wrong or missing ones turn a fifteen-second task into ninety seconds of typing.
Ask for less
Every field costs something, and the cost is paid in people who close the tab.
So the useful question about each one is: what will actually break if we don't ask this? Marketing wants a phone number, and nobody has decided who will call it. The form asks for a company name that appears nowhere afterwards. Registration wants a date of birth to check an age limit that could be checked with a yes/no.
Two related habits. Don't split what people think of as one thing: a single "name" field beats first/middle/last for most of the world, and one address box beats five. And accept the formats people actually type — spaces in card numbers, dashes in phone numbers, a trailing space from a paste. Stripping those yourself takes one line; making the user do it costs you some of them.
Validate at the right moment
Validation has a timing problem, and the wrong timing feels like being nagged.
Validating on every keystroke means telling someone their email is invalid while they're typing the third character of it. Validating only at submit means letting them fill twelve fields before revealing that the second one was wrong.
The rule that works: check when the person leaves the field, and after an error has appeared on a field, re-check as they type — so the error clears the moment it's fixed rather than at the next submit. Success is quiet; the absence of a complaint is enough.
And native validation gets you a long way with no code: required, type, min, max, pattern, minlength. It runs before submit, it's built in, and you can style the states with :invalid and :user-invalid — the second only fires after the person has actually interacted, which is what you almost always want.
One thing to be clear about: client-side validation is convenience, never protection. Anyone can bypass it in ten seconds with devtools. The server checks everything again, always — the Security sphere is where that story is properly told, but the rule is absolute.
Errors that help
A useful error message has three parts: what happened, why, and what to do about it. "Invalid input" has none of them. "This email is already registered — sign in instead, or use a different address" has all three.
Where it appears matters as much as what it says. The message goes at the field, not only in a summary at the top, and it needs to be reachable by assistive technology — the field gets aria-invalid, the message is linked with aria-describedby, so a screen reader reads the problem when the field is focused. On submit, move focus to the first field with an error; otherwise a keyboard user is told there's a problem somewhere in a form they must now hunt through.
And never, under any circumstances, clear the fields. Losing typed data is the single most infuriating thing a form can do, and it's always a bug, never a security measure.
The states nobody designs
A form has more states than "empty" and "filled," and the unloved ones are where trust is lost:
Submitting. The button must become unavailable and say something is happening, or people click three times and you create three orders. Every single time.
Failed after submitting. The network died with their data in flight. Keep everything they typed, say what happened, and let them retry without retyping.
Long forms interrupted. Someone's phone rings during a ten-minute application. Saving a draft to local storage as they go is a few lines of code and turns a lost application into a resumed one.
Succeeded. Say what happened and what's next. A form that just goes blank leaves people wondering whether to submit again.
Build the form once, properly
This is a textbook case for the How to Learn sphere's idea of building chunks on purpose. "A form" is one of the dozen structures you'll construct for the rest of your career, and the fastest way to own it is to build one reference implementation, deliberately, with every state present — empty, focused, filled, invalid, submitting, failed, succeeded — and then rebuild it from scratch a week later without looking at the first one.
The rebuild is the part that does the work, and it's the part everyone skips. Struggling to remember whether the error clears on input or on blur is exactly the effort that turns a set of separate facts into one block you'll deploy without thinking.
A form is a conversation where only one side can ask questions. The least you can do is ask few, ask clearly, and not lose the answers.
In practice
Fill in your own form on a phone, one-handed, as a stranger would. Not a test account with saved autofill — a fresh one, thumb only. Everything wrong with it surfaces in about ninety seconds.
Count the fields, then delete one. Ask what would genuinely break. Do it every time you build a form; the answer is often "nothing."
Turn JavaScript off and try to submit. If nothing happens at all, you built a form that only exists conditionally.
Test with the browser's autofill and with a password manager. Wrong autocomplete values show up instantly.
Write the error messages before the happy path. They're the part you'd otherwise write last, tired, in two words.
Keep a personal reference form with all seven states, and copy from it. This is your chunk, made explicit — and rebuilding it from memory once a quarter keeps it real.
Check yourself
Close the article and answer in your own words:
- Name four things a real
formelement gives you that a div with a click handler doesn't. - Why is a placeholder not an acceptable label?
- What does the
typeattribute change for a mobile user, and what doesautocompletechange? - When should a field be validated, and when should the error clear?
- Why is client-side validation never a security measure?
- What are the three parts of a useful error message, and where should it appear?
- List the form states beyond empty and filled, and say what goes wrong when each is skipped.
In short
- Forms are where products lose people, and where the platform has already done most of the work for you.
- Use a real
form: Enter to submit, password saving, one submit event, and behaviour that survives JavaScript failing. - Every field needs a real label tied with
for, the righttypefor the keyboard it summons, and a correctautocompletevalue. - Ask for less: every field costs users, one name field beats three, and accepting messy formats is one line of your code instead of friction for everyone.
- Validate on blur, re-validate as they fix it, use native validation attributes — and always check again on the server.
- Errors say what happened, why and what to do, appear at the field, are announced by assistive tech, and never wipe what was typed.
- Design the unloved states: submitting, failed, interrupted, succeeded.
- Build one reference form with every state, then rebuild it from memory a week later — that rebuild is what turns it into a chunk.