docs/handbook/forms
Forms
A guide to building forms with Base UI components.
Base UI form control components extend the native constraint validation API so you can build forms for collecting user input or providing control over an interface. They also integrate seamlessly with third-party libraries like React Hook Form and TanStack Form.
Naming form controls#
Form controls must have an accessible name in order to be recognized by assistive technologies. Use the label strategy below for each control type.
Input controls#
Use <Field.Label> or a native <label> to label the following controls:
InputNumberFieldOTPFieldAutocompleteCombobox(input outside popup)CheckboxRadioSwitch
You can implicitly label <Checkbox>, <Radio> and <Switch> components by enclosing them with <Field.Label>:
Trigger-based controls#
Combobox(input inside popup): use<Combobox.Label>.Select: use<Select.Label>.Slider: use<Slider.Label>. For multi-thumb sliders, also add anaria-labelon each<Slider.Thumb>to distinguish the thumbs.
Fallback#
If no visible label is rendered, provide aria-label on the actual form control.
Describing the control#
<Field.Description> automatically assigns an accessible description:
Labeling control groups#
Compose <Fieldset> when a single label applies to multiple controls, such as a range slider with multiple thumbs or a section that combines several inputs. For checkbox and radio groups, keep the group label in <Fieldset.Legend> and wrap each option with <Field.Item>:
<Field.Item> should enclose each checkbox or radio option so every control has its own label and description:
Building form fields#
Pass the name prop to <Field.Root> to include the wrapped control's value when a parent form is submitted:
Submitting data#
You can take over form submission using the native onSubmit, or custom onFormSubmit props:
When using onFormSubmit, you receive form values as a JavaScript object, with eventDetails provided as a second argument. Additionally, preventDefault() is automatically called on the native submit event:
Constraint validation#
Base UI form components support native HTML validation attributes for many validation rules:
requiredspecifies a required field.minLengthandmaxLengthspecify a valid length for text fields.patternspecifies a regular expression that the field value must match.stepspecifies an increment that numeric field values must be an integral multiple of.
Base UI form components use a hidden input to participate in native form submission and validation.
To anchor the hidden input near a control so the native validation bubble points to the correct area, ensure the component has been given a name, and wrap controls in a relatively positioned container for best results.
Custom validation#
You can add custom validation logic by passing a synchronous or asynchronous validation function to the validate prop, which runs after native validations have passed.
Use the validationMode prop to configure when validation is performed:
onSubmit(default) validates all fields when the containing<Form>is submitted, afterwards invalid fields revalidate when their value changes.onBlurvalidates the field when focus moves away.onChangevalidates the field when the value changes, for example, after each keypress in a text field or when a checkbox is checked or unchecked.
validationDebounceTime can be used to debounce the function in use cases such as asynchronous requests or text fields that validate onChange.
Server-side validation#
You can pass errors returned by (post-submission) server-side validation to the errors prop, which will be merged into the client-side field state for display.
This should be an object with field names as keys, and an error string or array of strings as the value. Once a field's value changes, any corresponding error in errors will be cleared from the field state.
When using Server Functions with Form Actions you can return server-side errors from useActionState to the errors prop. A demo is available here.
Displaying errors#
Use <Field.Error> without children to automatically display the field's native error message when invalid. The match prop can be used to customize the message based on the validity state, and manage internationalization from your application logic:
React Hook Form#
React Hook Form is a popular library that you can integrate with Base UI to externally manage form and field state for your existing components.
Initialize the form#
Initialize the form with the useForm hook, assigning the initial value of each field by their name in the defaultValues parameter:
Integrate components#
Use the <Controller> component to integrate with any <Field> component, forwarding the name, field, and fieldState render props to the appropriate part:
For React Hook Form to focus invalid fields when performing validation, you must ensure that any wrapping components forward the ref to the underlying Base UI component. You can typically accomplish this using the inputRef prop, or directly as the ref for components that render an input element like <NumberField.Input>.
Field validation#
Specify rules on the <Controller> in the same format as register options, and use the match prop to delegate control of the error rendering:
Submitting data#
Wrap your submit handler function with handleSubmit to receive the form values as a JavaScript object for further handling:
TanStack Form#
TanStack Form is a form library with a function-based API for orchestrating validations that can also be integrated with Base UI.
Initialize the form#
Create a form instance with the useForm hook, assigning the initial value of each field by their name in the defaultValues parameter:
Integrate components#
Use the <form.Field> component from the form instance to integrate with Base UI components using the children prop, forwarding the various field render props to the appropriate part:
The Base UI <Form> component is not needed when using TanStack Form.
Form validation#
To configure a native <form>-like validation strategy:
- Use the additional
revalidateLogichook and pass it touseForm. - Pass a validation function to the
validators.onDynamicprop on<form.Field>that returns an error object with keys corresponding to the fieldnames.
This validates all fields when the first submission is attempted, and revalidates any invalid fields when their values change again.
Field validation#
You can pass additional validator functions to individual <form.Field> components to add validations on top of the form-level validators:
Submitting data#
To submit the form:
- Pass a submit handler function to the
onSubmitparameter ofuseForm. - Call
form.handleSubmit()from an event handler such as formonSubmitoronClickon a button.