Validation
Per-Field Rules
Add validation rules to any field using the fluent API. The most common rules have dedicated methods:
TextInput::make('name')->required(),
TextInput::make('email')->email()->required(),
TextInput::make('age')->number()->nullable(),Adding Custom Rules
Use rule() to add a single rule, or rules() for multiple:
TextInput::make('username')
->required()
->rule('alpha_dash')
->rule('min:3')
->rule('max:20'),
TextInput::make('password')
->password()
->rules(['required', 'min:8', 'confirmed']),Rules are additive. The username field above validates with required, alpha_dash, min:3, and max:20; the later rule() calls do not replace required. The rules() method behaves the same way, and duplicate string rules are removed when the final rule list is collected.
You may pass any rule Laravel supports: strings, Rule objects, or closures:
use Illuminate\Validation\Rule;
TextInput::make('email')
->email()
->required()
->rule(Rule::unique('users', 'email')->ignore($user->id)),Convenience Methods
These shorthand methods are available on all fields:
| Method | Rule Added |
|---|---|
required() | required |
nullable() | nullable |
And on TextInput specifically:
| Method | Input Type + Rule |
|---|---|
email() | email type + string, email |
password() | password type + string |
number() | number type + numeric |
integer() | number type + integer |
url() | url type + string, url |
tel() | tel type + string |
min(5) | adds min:5 |
max(100) | adds max:100 |
Auto-Generated Rules
Some fields automatically add validation rules based on their configuration, so you do not have to duplicate logic.
Combobox And Radio
Providing options() automatically adds a Rule::in() constraint:
Combobox::make('country')
->options(['us' => 'United States', 'ca' => 'Canada', 'mx' => 'Mexico']),
// Auto-adds: Rule::in(['us', 'ca', 'mx'])Multiple Combobox
Enabling multiple() adds the array rule and applies Rule::in() to each element via fieldname.*:
Combobox::make('languages')
->options(['php' => 'PHP', 'js' => 'JavaScript', 'go' => 'Go'])
->multiple(),
// Auto-adds: 'array' on 'languages', Rule::in(['php', 'js', 'go']) on 'languages.*'Combobox Tokens
Calling tokens() stores an array of strings. Finite token options validate each token when custom tokens are disabled:
Combobox::make('topics')
->tokens()
->options(['Planning', 'Research', 'Review'])
->allowCustomValues(false),
// Auto-adds: 'array' on 'topics', Rule::in([...]) on 'topics.*'Custom Values And Server-Backed Combobox
Combobox keeps finite option validation unless allowCustomValues() or searchOptionsUsing() is enabled, or the field uses remote source() options. In those cases the complete option list is not submitted with the form config, so Rule::in() is skipped:
Combobox::make('framework')
->options(['laravel' => 'Laravel', 'rails' => 'Rails'])
->allowCustomValues(),
// No Rule::in() added. The user may type custom values.Slider
Calling min() and max() on a scalar slider adds min: and max: rules automatically:
Slider::make('rating')->min(1)->max(10),
// Auto-adds: 'min:1', 'max:10'For range() sliders, the submitted [min, max] array is validated with array, list, and size:2. Each item gets numeric, min, and max; minStepsBetween() adds a package rule for the thumb order and minimum gap.
For generated rules that are unique to one field, see that field's documentation.
The #[Validate] Attribute
The #[Validate] attribute lets you type-hint your form in a controller method and have it validated automatically:
use InertiaUI\Forms\Validate;
class UserController
{
public function store(#[Validate] CreateUserForm $form)
{
$validated = $form->validated();
User::create($validated);
return redirect()->route('users.index');
}
}The container creates the form, injects the current request, and calls validate() when it resolves a parameter with #[Validate]. Validation failures throw a ValidationException, and Inertia handles the redirect with errors automatically.
File upload fields may generate token or multipart file rules. The #[Validate] attribute validates those rules, but it does not hydrate temporary upload tokens into files. Read resolved SubmittedUpload objects through the helpers in File Uploads.
Accessing Validated Data
Use validated() to get only the validated data:
public function store(#[Validate] CreateUserForm $form)
{
$data = $form->validated();
// ['name' => 'Jane Example', 'email' => 'jane@example.com']
}Manual Validation
You may validate manually without the attribute:
public function store(Request $request)
{
$form = CreateUserForm::make();
$validated = $form->validated();
User::create($validated);
}The form automatically receives the current request when resolved through the container. Use validate() when you only need to run validation without reading transformed values.
Precognition
Laravel Precognition gives users real-time validation feedback as they fill out the form. Precognitive fields are validated on the server when the user leaves the field (on blur).
Marking Fields
Use precognitive() on any field:
TextInput::make('email')
->email()
->required()
->precognitive(),
TextInput::make('username')
->required()
->rule(Rule::unique('users'))
->precognitive(),Only fields marked as precognitive will be validated in real-time. Other fields are validated on submission as usual.
How It Works
A precognitive field blur triggers this flow:
- The frontend component sends a Precognition request to the form's action URL
- The
PrecognitionandPrecognition-Validate-Onlyheaders tell the server which fields to validate - Laravel resolves the action parameters and validates only the requested precognitive fields
- Validation errors are returned and displayed immediately under the field
The action route still needs Laravel's HandlePrecognitiveRequests middleware. For routes using an Inertia Forms form class, type-hint the form with #[Validate] so validation runs while Laravel resolves the action parameters. A FormRequest may also handle the request if it owns the same rules. You do not need a separate Precognition endpoint.
Custom Messages
Override the messages() method to customize error messages:
class CreateUserForm extends Form
{
public function fields(): array
{
return [
TextInput::make('email')->email()->required(),
TextInput::make('name')->required()->rule('min:2'),
];
}
public function messages(): array
{
return [
'email.required' => 'We need your email to create an account.',
'email.email' => 'That doesn\'t look like a valid email address.',
'name.min' => 'Your name must be at least :min characters.',
];
}
}Custom Attribute Names
Override the attributes() method to customize how field names appear in error messages:
public function attributes(): array
{
return [
'dob' => 'date of birth',
'tel' => 'phone number',
];
}Now instead of "The dob field is required", users see "The date of birth field is required".
Collecting Rules
The rules() method on the form collects rules from all authorized fields and returns them as a standard Laravel rules array:
$form = CreateUserForm::make();
$form->rules();
// [
// 'name' => ['required'],
// 'email' => ['required', 'string', 'email'],
// ]This is the same format you'd pass to Validator::make(). The form uses this internally for validation.
KeyValue Nested Validation
The KeyValue field stores keyed mode as an associative map and single mode as a value-only list. Use valueRules() for each submitted value:
KeyValue::make('metadata')
->valueRules(['required', 'string']);This adds:
[
'metadata' => ['array'],
'metadata.*' => ['required', 'string'],
]In keyed mode, keyRules() validates the submitted keys at the same nested value paths:
KeyValue::make('metadata')
->keyRules(['regex:/^[a-z_]+$/'])
->valueRules(['required', 'string']);Repeater Nested Validation
The Repeater field validates its nested schema fields using dot notation. Each schema field's rules are prefixed with items.*.:
Repeater::make('items')
->minItems(1)
->maxItems(10)
->schema([
TextInput::make('name')->required(),
TextInput::make('quantity')->number()->required()->rule('min:1'),
TextInput::make('price')->number()->required(),
]),This generates the following rules:
[
'items' => ['array', 'min:1', 'max:10'],
'items.*' => ['array'],
'items.*.name' => ['required'],
'items.*.quantity' => ['numeric', 'required', 'min:1'],
'items.*.price' => ['numeric', 'required'],
]Blocks Nested Validation
The Blocks field validates its structural shape first, then validates nested schema fields for the submitted block type at each request index. Submitted values use type and data keys:
Blocks::make('content')
->maxItems(10)
->sets([
BlockSet::make('hero')
->maxItems(1)
->schema([
TextInput::make('headline')->required(),
]),
BlockSet::make('quote')->schema([
Textarea::make('quote')->required(),
]),
]);A request with a hero at index 0 and a quote at index 1 generates request-indexed nested rules such as:
[
'content' => ['array', 'max:10', /* per-set max validation */],
'content.*' => ['array'],
'content.*.type' => ['required', 'string', Rule::in(['hero', 'quote'])],
'content.*.data' => ['required', 'array'],
'content.0.data.headline' => ['required'],
'content.1.data.quote' => ['required'],
]The top-level content value is always validated as an array. Blocks::maxItems() adds the normal Laravel max: rule to that array, while each BlockSet::maxItems() adds custom per-type validation that counts submitted items by type and fails when one set appears too many times. Nested schema rules are generated only for the block type submitted at each request index. See Blocks nested validation for the field-level explanation.