Forms
Layout, validation, errors and submission — the rules that make forms fast to complete and hard to get wrong.
Forms are where products ask people for effort. Every rule below exists to reduce that effort: fewer decisions, clearer errors, no lost work.
examples/patterns/signup-form.tsx
Layout
- One column. Multi-column forms break the reading path and hide fields. The exception is short, related pairs — first and last name, expiry and CVC.
- Labels above fields. Top-aligned labels are the fastest to scan and survive translation and zoom. Use
FieldLabel, never a placeholder, as the label. - Group with
FieldSet. Related fields share aFieldLegend; groups are separated by more space than fields within them. - Size the field to the answer. A postcode field shouldn't be as wide as an address field.
- Horizontal rows for settings. Settings pages pair a label and description on the left with the control on the right, using
Field orientation="responsive"so it stacks on narrow screens.
examples/patterns/settings-form.tsx
Required and optional
Mark the minority. Most forms should only ask for what's required — then label the few optional fields with “(optional)” and don't decorate required ones. When most fields are optional, mark the required ones instead. Never rely on an asterisk alone; it means nothing to many people and to screen readers.
Validation
When to validate
- On submit, validate everything and move focus to an error summary at the top of the form.
- After the first submit, re-validate a field when people leave it (
blur) so errors disappear as soon as they're fixed. - Never on every keystroke before a submit — being told an email is invalid after typing one letter is hostile.
How to show errors
- Put the message directly under the field with
FieldError, which renders an icon and text — never color alone. - Mark the control with
aria-invalidand the field withdata-invalid. - Say what's wrong and how to fix it: Enter an email address like ada@acme.co, not Invalid input.
- For forms longer than one screen, add an
Alertsummary that lists the problems and receives focus.
<Field data-invalid>
<FieldLabel htmlFor="email">Work email</FieldLabel>
<Input id="email" aria-invalid />
<FieldError>Enter an email address like ada@acme.co.</FieldError>
</Field>Submission
- Keep the submit button enabled. A disabled button can't explain what's missing; submitting and showing errors can.
- Show progress on the button with
loading. It keeps its width and focus, and blocks double submission. - Confirm success with a toast when people stay on the page, or navigate to the result.
- Never clear the form on error. Server errors are shown inline or in a summary, and every value is preserved.
Add a domain ending, like .com.
Do.Specific guidance next to the field, with an icon.
Don’t.A red border alone fails people who can't see the color — and explains nothing.
Accessibility checklist
- Every control has a programmatic label (
htmlFor+id, oraria-labelfor icon-only controls). - Help text is linked with
aria-describedby; errors are announced (FieldErrorusesrole="alert"). autocompleteattributes are set for personal data (name,email,new-password), so browsers and password managers can help.- The form is fully operable with the keyboard, in a logical order, with visible focus.