/

Forms in React: When to Validate and Where the Error Goes

Lazy first, eager after failure. The asymmetry that separates a helpful form from a critical one, plus what server actions changed.

Serafim Korablev
Serafim Korablev
@korablev

Forms are where most products lose people, and almost never because the inputs looked wrong. They lose them at the moment of failure: an error that appears in the wrong place, at the wrong time, saying the wrong thing, on a field the reader has already left.

1,663 components on 21st carry the form tags. The layouts are solved. The behaviour is what this is about.

The shapes

Contact Card by Efferd is the simplest useful form. Multistep Form by Arihant jain and Multistep Form by Jatin Yadav split a long one, which the onboarding guide covers.

CreditCardForm by Rahil Vahora and Modern Payment Form by Ruixen UI are the payment shapes - with the caveat from the checkout guide that real card fields belong to your payment provider, not to your codebase.

Ride Booking Form by Ravi Katiyar and Onboarding Form by the same author are the domain-shaped ones.

When to validate

The single most consequential decision in a form, and most implementations get it backwards.

Not while typing the first time. A field that turns red on the third character of an email address is hostile. The reader is not finished.

On blur, once. When they leave the field, check it. This is the moment they have declared themselves done with it.

On change, after it has failed once. Now live feedback helps, because it tells them the moment they have fixed it. This asymmetry - lazy first, eager after failure - is what separates a form that feels helpful from one that feels like a critic.

On submit, for everything. Including the server's rules, which the client cannot know.

Where the error goes

Beside the field, always. Not in a toast in the corner, which tells the reader something is wrong and hides where, and not only at the top, which makes them hunt.

Wire it properly: aria-invalid on the input, aria-describedby pointing at the message, so a screen reader reads the error together with the field rather than as an unrelated announcement. And write the fix, not the fault: "Enter a date in the future" beats "Invalid date".

For a long form, add an error summary at the top - a list of the failures, each one a link to its field - and move focus to it on submit. That is the pattern that stops a reader submitting a twenty-field form and hunting for the one red border below the fold.

Server actions and the parts that changed

Server-side form handling is now the default in the React ecosystem, and it changes three things worth knowing.

The form works before hydration. A real <form> with an action submits with plain HTML, which means the fastest possible interaction and a form that works while JavaScript is still loading. That is a genuine improvement and it is easy to lose by intercepting onSubmit.

Pending state comes from the framework. The hooks that report whether a submission is in flight replace the manual isSubmitting boolean and, importantly, they work without turning the whole page into a client component.

Server errors are the same shape as client errors. Return them in the same structure, render them in the same place. A validation library shared between both is what stops two sets of rules drifting apart.

Six details that matter more than the layout

One column. Multi-column forms are read in the wrong order and break at every breakpoint. The exception is genuinely paired data, like city and postcode.

Label above the field. Placeholder labels vanish on typing and float labels have contrast problems at small sizes.

Mark the optional ones, not the required ones, when most fields are required. The asterisk convention makes a form look longer than it is.

Never disable submit. A disabled button with no explanation is a dead end; let them submit and show what is wrong.

Autofill attributes. The single highest-leverage thing on any form with a name and an address, covered in the inputs guide.

Save the draft. For anything longer than a screen, keep what they typed in local storage. A refresh that empties a long form is the most expensive bug in this category.

Where else to look

The honest list, because the answer is not always us:

SourceBest forTrade-off
shadcn/ui FormReact Hook Form wired to the primitives with the ARIA correctThe layout and the copy are yours
React Hook Form + ZodValidation shared between client and serverNot a visual layer
ConformProgressive enhancement with server actionsNewer, and a different mental model
21stContact, payment, multi-step and domain-shaped formsQuality varies by author, so preview before you take it

Taking one

Every component page has a live preview and the code. Installing goes through the shadcn CLI against our registry:

bash

That key comes from your 21st account, and installs require a membership. Set API_KEY_21ST once in your shell and the command works for anything in the catalogue. The 21st MCP covers the same ground without leaving the editor: ask Claude, Cursor or Codex for a form, get the previews inline, and let the agent write the file.

Browse form components →

Frequently asked

When should a form validate a field?
On blur the first time, because that is when the reader has declared themselves done with it; then on change once it has already failed, so they see the moment it is fixed; and on submit for everything, including the server's rules. Validating while someone types the first time is the single most hostile thing a form does.
Where should form errors appear?
Beside the field, with aria-invalid on the input and aria-describedby pointing at the message so a screen reader reads them together. Not in a toast in the corner, which says something is wrong and hides where. For a long form, add a summary at the top that links to each failed field and move focus to it on submit.
What did server actions change about forms?
Three things. A real form with an action submits before hydration, so it works while JavaScript is still loading. Pending state comes from framework hooks rather than a manual boolean. And server errors can share the shape of client errors, which a validation schema used on both sides keeps from drifting.
Should the submit button be disabled until the form is valid?
No. A disabled button with no explanation is a dead end, and the reader cannot tell what is missing. Let them submit, then show what is wrong, move focus to the first failure, and keep everything they typed.

Published

Aug 20, 2026

Read time

6 min

Tags

GuideFormsReactAccessibility

Share