Form validation

You can validate forms in two places: the client and the server.

We recommend doing both for different reasons:

  1. Validate fields on the client for a better user experience, field errors are displayed immediately
  2. 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.

Please enter your username

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.

Please enter your username
Please enter your password

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"
/>
PropTypeDescription
titleReactNodeThe title of the success message.
messageReactNodeThe message to display.