Web Engineering

Forms deserve the boring states

2 min read

one question at a time

A form gets better when the team stops pretending the empty state is the whole story.

The user arrives with no data, types, hesitates, makes a mistake, fixes it, submits, gets a rejection, retries, and sometimes comes back to the draft later. That is the actual interaction. If we only design the empty happy path, the implementation invents the rest and usually gets some of it wrong.

what the machine needs to know

Even a small form has a state graph hiding under it. Empty. Dirty. Invalid. Waiting. Failed. Saved. Restored. Permission blocked.

Those labels do not need to leak into the UI. They do need to exist in the implementation conversation, because once they do, it becomes harder to hand-wave away the awkward cases.

the states are where trust shows up

Empty should give the user enough context to begin without guesswork. Dirty should protect the work if the page changes or the tab closes. Invalid should point to the right thing to fix, not just announce that something is wrong. Pending should keep the request alive without letting the user fire the same intent twice. Success should say what actually happened, especially when a background job or side effect started.

recovery tells the truth

The best form is the one that survives interruption. Can the user edit one field without redoing the rest? Can they tell whether a submit succeeded before the timeout? Can they return to a draft? Can they recover from a partial save? If those answers are yes, the form is probably doing real work instead of staging a tidy screenshot.

Jeremy London

About Jeremy London

Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.