Forms & validation
How to build accessible forms with Field and Form: labels and errors wired for you, the browser's own validation, your own rules, and errors from the server.
The pieces
| Part | What it does |
|---|---|
Field.Root | Holds one field's state and links its parts. Takes name, required, disabled, invalid, validate and validationMode. |
Field.Label | A label pointed at the control. |
Field.Control | The input. Pass asChild to use TextField, TextArea, a native select or your own control. |
Field.Description | Help text, added to the control's accessible description. |
Field.Error | The error message. Renders only while the field is invalid and is announced when it appears. |
Form | Validates every Field on submit, focuses the first invalid one, and shows server errors on the right field. |
A field that validates
The browser already knows how to check required, type="email", pattern, min, max and length limits. Field uses those checks and shows the result in Field.Error. Without children, the error shows the browser's message.
Other Rad UI controls
Checkbox, Switch, Select and NumberField can sit directly inside Field.Root, without Field.Control. They take the field's id, name, disabled and required state, and its description and error.
One message per problem
match shows an error only for one validity problem, so each can say something specific.
The keys are the browser's ValidityState flags: valueMissing, typeMismatch, patternMismatch, tooShort, tooLong, rangeUnderflow, rangeOverflow, stepMismatch, badInput and customError.
Your own rules
validate runs after the browser's checks pass. Return a message, several messages, or null. The message is also handed to the browser, so a plain <form> won't submit either.
For a checklist such as password rules, return every failing message and render them with Field.Validity:
Async checks
validate can return a promise, for checks that need the server. While it's pending, Field parts carry data-validating and Field.Validity reports validating. A slower, older check never overwrites a newer result. Add validationDebounceTime so it waits for typing to pause.
A Form submit waits for pending checks: it treats the field as not yet valid and focuses it.
When errors show
validationMode | Errors first appear |
|---|---|
onSubmit (default) | after a submit attempt |
onBlur | when the control loses focus |
onChange | as the user types |
Once an error is showing, it updates as the user types, so it disappears as soon as the value is fixed. Set the mode on each Field, or once on Form.
Submitting with Form
Form turns off the browser's validation bubbles and validates every Field itself. If any are invalid, it shows their errors and focuses the first one. Otherwise onFormSubmit receives the values.
Errors from the server
Pass server errors to errors, keyed by field name. Each shows on its Field and clears as soon as the user edits that field.
Without Form, set invalid on the Field and put the message in Field.Error.
Accessibility checklist
- Every control has a
Field.Label(oraria-label) - Help text is in
Field.Description, not placeholder text - Errors say how to fix the problem, not only that there is one
- Required fields are marked with
required, not only an asterisk - The first invalid field receives focus after a failed submit (Form does this)
Related: Field, Form, Controlled & Uncontrolled Forms, Accessibility.