Guides
Validation
Validate a form field, and write the message that tells someone how to fix it.
Every Luke UI form field can report a problem. An invalid field takes the danger border colour and renders a message beneath the control.
Pass errorMessage for a controlled error from a form library, your server, or your own state. The
field is invalid as soon as you pass a non-empty message. When the field should generate its own
message instead, from a constraint or a validate rule, leave errorMessage out entirely.
Let the browser validate
Set isRequired on a field or radio group to require a value before the form submits. Pass
minLength, maxLength, or pattern on TextInputField to state a rule for the text. Pass
type="email" or type="url" to check the value's format.
Leave out errorMessage. The field renders the message the constraint produces. Constraint errors
normally appear after someone blurs a field or submits the form.
Native validation is the default. It prevents form submission when a field fails validation, so a form-library submit handler never runs.
Set validationBehavior="aria" on Form when a form library owns
validation. This lets the library receive the submit event and decide whether the form is valid. An
individual field can override the Form setting.
Native validation messages
Constraint messages come from the browser. They follow the browser or operating system language setting.
Keep the browser message when it matches the product language and names the fix clearly enough. Pass your own copy when the message must use the field's wording, match a style guide, or name a product rule the browser cannot describe.
On composed fields such as TextInputField, errorMessage is React content for a controlled error.
To customise a constraint message from the field's validity state, compose the
field primitives and pass a render function to FieldError. For a
rule the browser cannot check, prefer validate on the field instead.
Custom validation
Pass a validate function for a rule the browser cannot check, such as a reserved username. The
function takes the value and returns the validation message, or null when the value is valid.
Leave out errorMessage. The field renders the message validate returns. Those messages normally
appear after blur or submit, not between keystrokes.
Return one actionable message, even when several conditions could fail. Check the most important condition first so the next message appears once that one is fixed. The field joins multiple messages with spaces rather than rendering a list.
Realtime validation
Pass a controlled errorMessage for feedback that must update on every keystroke, such as password
strength. You decide validity here, not the field. Make the field controlled with value and
onChange, and compute errorMessage from that value on every render. An absent message keeps the
field valid, so undefined, null, false, and an empty string all mean no error. The message
appears when the application supplies a non-empty value and clears when that message is removed.
const [password, setPassword] = useState('');
const isTooShort = password.length > 0 && password.length < 12;
<TextInputField
errorMessage={isTooShort ? 'Passwords need at least 12 characters.' : undefined}
label="Password"
onChange={setPassword}
type="password"
value={password}
/>;Form libraries
Pass the library's current error message as errorMessage. The field goes invalid as soon as the
library reports one, and clears once the library clears it. Set validationBehavior="aria" on
Form so native validation does not block the library's submit handler.
Read React Hook Form and TanStack Form for the full setup.
Server validation
Pass server errors through Luke UI's Form as validationErrors, keyed
by each field's name. Server errors appear immediately. Form clears a field's server error when
the value is committed, for example when the field blurs after an edit.
A React Action can return those errors with the submitted values. Pass the Action to Form's
action, feed validationErrors from useActionState, and restore each field with defaultValue
so values survive an unsuccessful response. After a successful Action, React resets uncontrolled
fields. Pass the pending state to the submit Button.
Try the username admin to see a server error. Change the value and leave the field to clear it,
then choose another name to submit successfully.
A non-empty field-level errorMessage overrides that field's entry in validationErrors. Clearing
the field error reveals the outstanding server error if the value has not been committed. Committing
a new value also clears that field's server error.
Focus after validation
With native validation, an unsuccessful submission focuses the first invalid field. Keep that default when each field's message is enough to fix the problem.
Override it when the form shows an error summary, or when focus should land somewhere else first.
Pass onInvalid on Form and call preventDefault to stop that focus
move, then move focus yourself. See
React Aria's Form focus management for an
error-summary pattern.
Server errors from validationErrors appear after the Action returns. They do not use that
invalid-event focus path. Form libraries often manage focus themselves. Read
React Hook Form when that library owns
validation.
Always pair an invalid field with a message
An error message reports the problem when someone cannot distinguish the danger border from the resting border. It shows an error icon before its text.
Give every invalid field a message, as WCAG 1.4.1 Use of Colour requires. Let a constraint or
validate generate the message, or pass your own through errorMessage. The primitive roots take
isInvalid directly, so pair that state with a message yourself.
Write the message
An error message has two jobs: name the problem, and name the fix.
errorMessage takes any React content, so a message with a link or emphasis works directly.
| Instead of | Write |
|---|---|
| Invalid input | Enter a valid email address. |
| This field is required | Choose a country. |
| Error | Passwords need at least 12 characters. |
- Say what to do next, not only what went wrong. "Choose a delivery date" beats "No date selected".
- Use the field's own words. If the label says "Country", the message says country, not region.
- Keep it to one sentence. A long message pushes the rest of the form down the page.
- Describe what is wrong with the value. Do not blame the reader. "Enter a date after today" rather than "You entered an invalid date".
- Repeat anything someone needs from
description. They may never look back up.