Forms
Form Validation
Wire Forms provides validation at three levels: field-level rules, form-level rules, and programmatic validation via the Core ValidationPipeline.
Wire Forms provides validation at three levels: field-level rules, form-level rules, and programmatic validation via the Core ValidationPipeline.
Field-Level Rules
Each field declares its own validation rules:
TextInput::make('name') ->required() ->maxLength(255) ->rules(['string', 'regex:/^[a-zA-Z\s]+$/']); TextInput::make('email') ->email() ->required() ->rules('unique:users,email'); TextInput::make('age') ->numeric() ->rules(['integer', 'min:18', 'max:120']); Select::make('role') ->required() ->rules('in:admin,editor,viewer');
Built-in Rule Helpers
Some fields provide fluent helpers that map to Laravel rules:
| Method | Equivalent Rule |
|---|---|
->required() |
required |
->email() |
email |
->numeric() |
numeric |
->integer() |
integer |
->maxLength(255) |
max:255 |
->minLength(3) |
min:3 |
->url() |
url |
->tel() |
sets tel HTML input type (no validation rule) |
Custom Validation Messages
TextInput::make('name') ->required() ->validationMessages([ 'required' => 'Please enter a name.', 'max' => 'Name is too long.', ]);
Form-Level Rules
Add rules at the form level that span multiple fields:
Form::make() ->schema([ TextInput::make('password')->password()->required(), TextInput::make('password_confirmation')->password()->required(), ]) ->validationMessages([ 'password.confirmed' => 'Passwords do not match.', ]);
Programmatic Validation
validate()
Validates state against all collected rules and returns validated data:
// In a Livewire componentpublic function save(): void{ $data = $this->form->validate(); // $data contains only validated fields // Throws Illuminate\Validation\ValidationException on failure}
getValidationRules()
Inspect the collected rules without validating:
$rules = $this->form->getValidationRules();// ['name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email'], ...]
Validation in Save Lifecycle
When calling $form->save(), validation happens automatically as the first step:
save()├── 1. Validate ← all field + form rules├── 2. mutateDataBeforeSave()├── 3. Plugin hook: form.saving├── 4. beforeSave()├── 5. Persist (create/update)├── 6. Save relationships├── 7. afterSave()├── 8. Plugin hook: form.saved└── 9. Success notification
If validation fails, save() throws ValidationException and steps 2-9 are skipped.
Standalone Validation (without Livewire)
$form = Form::make() ->schema([ TextInput::make('name')->required(), TextInput::make('email')->email()->required(), ]) ->state(['name' => '', 'email' => 'not-an-email']); try { $data = $form->validate();} catch (ValidationException $e) { $errors = $e->errors(); // ['name' => ['The name field is required.'], 'email' => ['The email field must be a valid email address.']]}
Conditional Rules
Rules can use Closures for dynamic validation:
TextInput::make('company_name') ->required(fn () => $this->form()->getState()['type'] === 'business') ->rules(fn () => $this->isEditing() ? 'unique:companies,name,' . $this->getModel()->id : 'unique:companies,name'); Select::make('department') ->visible(fn () => $this->form()->getState()['type'] === 'business') ->required(fn () => $this->form()->getState()['type'] === 'business');
Error Display
Validation errors are automatically bound to Livewire's error bag and displayed next to their respective fields. The state path prefix is applied automatically:
// If statePath('data') and field is TextInput::make('name')// Error key: data.name// Livewire displays: @error('data.name')
No manual error rendering is needed in Blade.