Form validation
You can validate forms in two places: the client and the server.
We recommend doing both for different reasons:
- Validate fields on the client for a better user experience, field errors are displayed immediately
- Validate on the server for security, to prevent malicious users from submitting invalid data
Client-side validation
All form input components support client-side validation via the validate prop.
<TextField
id="username"
name="username"
label="Username"
validate={(value) => {
if (!value) {
return 'Please enter your username';
}
}}
/>The validate function is called anytime the value changes. The error message
returned from validate will be displayed underneath the input when the form is submitted.
Field error summary
Individual input fields will display their own validation errors inline. However, this approach does not give a very good experience to screen reader users. For screen readers it's better to display a summary of field errors in a single place so that they can see a high-level summary of all the errors in one place.
This is achieved by rendering a FieldErrorSummary component at the top of the form.
<FormRoot>
<FormStatus>
<FieldErrorSummary
fieldLabels={{ username: 'Username', password: 'Password' }}
title="There are errors in your submission"
subtitle="Please correct your input in these fields:"
/>
</FormStatus>
<Form>
<TextField
id="username"
name="username"
label="Username"
isRequired
validate={(value) => {
if (!value) {
return 'Please enter your username';
}
}}
/>
<TextField
id="password"
name="password"
label="Password"
isRequired
validate={(value) => {
if (!value) {
return 'Please enter your password';
}
}}
/>
<Button type="submit">Log in</Button>
</Form>
</FormRoot>Server error
If something goes wrong on the server, you can throw an error in the submit
handler along with an error message. The error message will be displayed in the
<FormServerError /> component.
import { FormAction } from '@/ui/components/forms/types';
export const loginAction: FormAction = async (currentState, formData) => {
const values = getFormValues<{ email: string; password: string }>(formData);
try {
await login(values.email, values.password);
} catch (error) {
throw new Error('Failed to login'); // This will be displayed in the <FormServerError /> component
}
};And then displayed in the form component like this:
const form = useForm<{ email: string; password: string }>({
onSubmit: async ({ values }) => {
await loginAction(values);
},
});
return (
<FormRoot context={form}>
<FormStatus>
{form.serverError && (
<FormServerError
title="An error occurred"
errorMessage={form.serverError}
/>
)}
</FormStatus>
<Form>
{/* Form fields */}
</Form>
</FormRoot>
);Success message
If the form is submitted successfully, you can display a success message using the <FormSuccessMessage /> component.
<FormSuccessMessage
title="Success!"
message="The form was submitted successfully"
/>| Prop | Type | Description |
|---|---|---|
title | ReactNode | The title of the success message. |
message | ReactNode | The message to display. |
Success!
The form was submitted successfully