Validation

Validation in Livewire should feel similar to standard form validation in Laravel. In short, Livewire provides a $rules property for setting validation rules on a per-component basis, and a ->validate() method for validating a component's properties using those rules.

Here's a simple example of a form in Livewire being validated.

class ContactForm extends Component
{
    public $name;
    public $email;

    protected $rules = [
        'name' => 'required|min:6',
        'email' => 'required|email',
    ];

    public function submit()
    {
        $this->validate();

        // Execution doesn't reach here if validation fails.

        Contact::create([
            'name' => $this->name,
            'email' => $this->email,
        ]);
    }
}
<form wire:submit.prevent="submit">
    <input type="text" wire:model="name">
    @error('name') <span class="error">{{ $message }}</span> @enderror

    <input type="text" wire:model="email">
    @error('email') <span class="error">{{ $message }}</span> @enderror

    <button type="submit">Save Contact</button>
</form>

If validation fails, a standard ValidationException is thrown (and caught by Livewire), and the standard $errors object is available inside the component's view. Because of this, any existing code you have, likely a Blade include, for handling validation in the rest of your application will apply here as well.

You can also add custom key/message pairs to the error bag.

$this->addError('key', 'message')

Real-time Validation

Sometimes it's useful to validate a form field as a user types into it. Livewire makes "real-time" validation simple with the $this->validateOnly() method.

To validate an input field after every update, we can use Livewire's updated hook:

class ContactForm extends Component
{
    public $name;
    public $email;

    protected $rules = [
        'name' => 'required|min:6',
        'email' => 'required|email',
    ];

    public function updated($propertyName)
    {
        $this->validateOnly($propertyName);
    }

    public function saveContact()
    {
        $validatedData = $this->validate();

        Contact::create($validatedData);
    }
}
<form wire:submit.prevent="saveContact">
    <input type="text" wire:model="name">
    @error('name') <span class="error">{{ $message }}</span> @enderror

    <input type="text" wire:model="email">
    @error('email') <span class="error">{{ $message }}</span> @enderror

    <button type="submit">Save Contact</button>
</form>

Let's break down exactly what is happening in this example:

  • The user types into the "name" field
  • As the user types in their name, a validation message is shown if it's less than 6 characters
  • The user can switch to entering their email, and the validation message for the name still shows
  • When the user submits the form, there is a final validation check, and the data is persisted.

If you are wondering, "why do I need validateOnly? Can't I just use validate?". The reason is because otherwise, every single update to any field would validate ALL of the fields. This can be a jarring user experience. Imagine if you typed one character into the first field of a form, and all of a sudden every single field had a validation message. validateOnly prevents that, and only validates the current field being updated.

Validating with rules outside of the $rules property

If for whatever reason you want to validate using rules other than the ones defined in the $rules property, you can always do this by passing the rules directly into the validate() and validateOnly() methods.

class ContactForm extends Component
{
    public $name;
    public $email;

    public function updated($propertyName)
    {
        $this->validateOnly($propertyName, [
            'name' => 'min:6',
            'email' => 'email',
        ]);
    }

    public function saveContact()
    {
        $validatedData = $this->validate([
            'name' => 'required|min:6',
            'email' => 'required|email',
        ]);

        Contact::create($validatedData);
    }
}

Customize Error Message & Attributes

If you wish to customize the validation messages used by a Livewire component, you can do so with the $messages property.

If you want to keep the default Laravel validation messages, but just customize the :attribute portion of the message, you can specify custom attribute names using the $validationAttributes property.

class ContactForm extends Component
{
    public $email;

    protected $rules = [
        'email' => 'required|email',
    ];

    protected $messages = [
        'email.required' => 'The Email Address cannot be empty.',
        'email.email' => 'The Email Address format is not valid.',
    ];

    protected $validationAttributes = [
        'email' => 'email address'
    ];

    public function updated($propertyName)
    {
        $this->validateOnly($propertyName);
    }

    public function saveContact()
    {
        $validatedData = $this->validate();

        Contact::create($validatedData);
    }
}

If you are not using the global $rules validation property, then you can pass custom messages and attributes directly into validate().

class ContactForm extends Component
{
    public $email;

    public function saveContact()
    {
        $validatedData = $this->validate(
            ['email' => 'required|email'],
            [
                'email.required' => 'The :attribute cannot be empty.',
                'email.email' => 'The :attribute format is not valid.',
            ],
            ['email' => 'Email Address']
        );

        Contact::create($validatedData);
    }
}

Direct Error Message Manipulation

The validate() and validateOnly() method should handle most cases, but sometimes you may want direct control over Livewire's internal ErrorBag.

Livewire provides a handful of methods for you to directly manipulate the ErrorBag.

From anywhere inside a Livewire component class, you can call the following methods:

// Quickly add a validation message to the error bag.
$this->addError('email', 'The email field is invalid.');

// These two methods do the same thing, they clear the error bag.
$this->resetErrorBag();
$this->resetValidation();

// If you only want to clear errors for one key, you can use:
$this->resetValidation('email');
$this->resetErrorBag('email');

// This will give you full access to the error bag.
$errors = $this->getErrorBag();
// With this error bag instance, you can do things like this:
$errors->add('some-key', 'Some message');

Testing Validation

Livewire provides useful testing utilities for validation scenarios. Let's a write a simple test for the original "Contact Form" component.

/** @test  */
public function name_and_email_fields_are_required_for_saving_a_contact()
{
    Livewire::test('contact-form')
        ->set('name', '')
        ->set('email', '')
        ->assertHasErrors(['name', 'email']);
}

This is useful, but we can take it one step further and actually test against specific validation rules:

/** @test  */
public function name_and_email_fields_are_required_for_saving_a_contact()
{
    Livewire::test('contact-form')
        ->set('name', '')
        ->set('email', '')
        ->assertHasErrors([
            'name' => 'required',
            'email' => 'required',
        ]);
}

Livewire also offers the inverse of assertHasErrors -> assertHasNoErrors():

/** @test  */
public function name_field_is_required_for_saving_a_contact()
{
    Livewire::test('contact-form')
        ->set('name', '')
        ->set('email', 'foo')
        ->assertHasErrors(['name' => 'required'])
        ->assertHasNoErrors(['email' => 'required']);
}

For more examples of supported syntax for these two methods, take a look at the Testing Docs.

Custom validators

If you wish to use your own validation system in Livewire, that isn't a problem. Livewire will catch ValidationException and provide the errors to the view just like using $this->validate().

For example:

use Illuminate\Support\Facades\Validator;

class ContactForm extends Component
{
    public $email;

    public function saveContact()
    {
        $validatedData = Validator::make(
            ['email' => $this->email],
            ['email' => 'required|email'],
            ['required' => 'The :attribute field is required'],
        )->validate();

        Contact::create($validatedData);
    }
}
<div>
    Email: <input wire:model.lazy="email">

    @if($errors->has('email'))
        <span>{{ $errors->first('email') }}</span>
    @endif

    <button wire:click="saveContact">Save Contact</button>
</div>
You might be wondering if you can use Laravel's "FormRequest"s. Due to the nature of Livewire, hooking into the http request wouldn't make sense. For now, this functionality is not possible or recommended.
← Previous Topic

Nesting Components

Next Topic →

File Uploads