Documentation Index
Fetch the complete documentation index at: https://mintlify.com/mui/base-ui/llms.txt
Use this file to discover all available pages before exploring further.
The Field component provides validation, error handling, and accessibility features for form inputs.
import * as Field from '@base-ui/react/Field';
Basic Usage
import * as Field from '@base-ui/react/Field';
function MyField() {
return (
<Field.Root name="email">
<Field.Label>Email</Field.Label>
<Field.Control
type="email"
placeholder="Enter your email"
required
/>
<Field.Description>We'll never share your email.</Field.Description>
<Field.Error match="valueMissing">Email is required</Field.Error>
<Field.Error match="typeMismatch">Please enter a valid email</Field.Error>
</Field.Root>
);
}
Sub-components
Field.Root
Groups all parts of the field. Renders a <div> element.
Key Props:
name: Identifies the field when a form is submitted
disabled: Whether the component should ignore user interaction (default: false)
validate: A function for custom validation that returns error message(s) or null
validationMode: When to validate - 'onSubmit' | 'onBlur' | 'onChange' (default: 'onSubmit')
validationDebounceTime: Debounce time in milliseconds for onChange validation (default: 0)
invalid: Whether the field is invalid (for external control)
dirty: Whether the field value has changed from initial value (for external control)
touched: Whether the field has been touched (for external control)
actionsRef: Ref to imperative actions like validate()
State attributes:
data-disabled: Present when disabled
data-touched: Present when field has been touched
data-dirty: Present when value has changed
data-valid: Present when field is valid
data-invalid: Present when field is invalid
data-filled: Present when field has a value
data-focused: Present when field is focused
Field.Label
Renders a <label> element associated with the control.
Field.Control
The input control element. Renders a native <input> by default.
Props:
- Accepts all standard input attributes (
type, placeholder, required, etc.)
disabled: Can override the Field.Root disabled state
name: Can override the Field.Root name
Field.Description
Renders descriptive text about the field. Automatically linked to the control via aria-describedby.
Field.Error
Displays error messages based on validation state.
Key Props:
match: Which validation error to display. Can be:
'badInput' | 'customError' | 'patternMismatch' | 'rangeOverflow' | 'rangeUnderflow' | 'stepMismatch' | 'tooLong' | 'tooShort' | 'typeMismatch' | 'valueMissing'
- A function:
(errors, value) => boolean
forceShow: Force error to display regardless of field state
Field.Validity
Accesses the field’s validity data programmatically (render prop pattern).
Field.Item
Used for grouping multiple controls within a single field (e.g., radio buttons, checkboxes).
Validation
Built-in Validation
The Field component automatically validates based on native HTML5 constraints:
<Field.Root name="age">
<Field.Label>Age</Field.Label>
<Field.Control
type="number"
required
min="18"
max="120"
/>
<Field.Error match="valueMissing">Age is required</Field.Error>
<Field.Error match="rangeUnderflow">Must be at least 18</Field.Error>
<Field.Error match="rangeOverflow">Must be less than 120</Field.Error>
</Field.Root>
Custom Validation
Provide a validate function that returns error message(s) or null:
function validatePassword(value: unknown) {
const password = String(value);
if (password.length < 8) {
return 'Password must be at least 8 characters';
}
if (!/[A-Z]/.test(password)) {
return 'Password must contain an uppercase letter';
}
return null;
}
<Field.Root name="password" validate={validatePassword}>
<Field.Label>Password</Field.Label>
<Field.Control type="password" />
<Field.Error match="customError" />
</Field.Root>
Async Validation
Async validation functions are supported:
async function checkUsernameAvailable(value: unknown, formValues: Form.Values) {
const username = String(value);
const response = await fetch(`/api/check-username?username=${username}`);
const { available } = await response.json();
return available ? null : 'Username is already taken';
}
<Field.Root name="username" validate={checkUsernameAvailable}>
<Field.Label>Username</Field.Label>
<Field.Control />
<Field.Error match="customError" />
</Field.Root>
Validation Modes
// Validate on blur
<Field.Root name="email" validationMode="onBlur">
{/* ... */}
</Field.Root>
// Validate on every change
<Field.Root name="email" validationMode="onChange">
{/* ... */}
</Field.Root>
// Validate on change with debounce
<Field.Root
name="email"
validationMode="onChange"
validationDebounceTime={300}
>
{/* ... */}
</Field.Root>
Styling
.Field-root[data-invalid] {
border-color: red;
}
.Field-root[data-valid] {
border-color: green;
}
.Field-control:focus {
outline: 2px solid blue;
}
.Field-error {
color: red;
font-size: 0.875rem;
margin-top: 0.25rem;
}
.Field-label {
font-weight: 500;
margin-bottom: 0.25rem;
}
.Field-description {
color: gray;
font-size: 0.875rem;
}
Imperative API
Trigger validation programmatically:
function MyForm() {
const actionsRef = React.useRef<Field.Actions>(null);
return (
<Field.Root name="email" actionsRef={actionsRef}>
<Field.Label>Email</Field.Label>
<Field.Control type="email" />
<Field.Error match="typeMismatch">Invalid email</Field.Error>
<button type="button" onClick={() => actionsRef.current?.validate()}>
Validate Now
</button>
</Field.Root>
);
}