Currently, our form doesn't have any validation after submitting, so let's add a few rules.
Validation Before Submit
The most straightforward way is just to call $this->validate()
with validation rules as a parameter in a typical Laravel syntax. And yes, all the same Laravel validation rules can be used.
public function save(): void{ $this->validate([ 'name' => 'required|min:3', ]); Company::create([ 'name' => $this->name, 'country_id' => $this->country, 'city_id' => $this->city, ]);
Now, let's show the validation errors in the Livewire Blade file. Again, the syntax of the @error
directive is the same as you would do it in the default Laravel. That's another proof that Livewire is excellent if you want to stay within the "comfort zone" of Laravel instead of learning new JS syntax.
resources/views/livewire/company-create.blade.php
<form wire:submit="save"> <div class="mb-4"> <label for="name" class="block text-gray-700">Company name</label> <input wire:model="name" type="text" required id="name" class="w-full p-2 mt-1 border rounded border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"> @error('name') <span class="mt-2 text-sm text-red-600">{{ $message }}</span> @enderror </div>
As a result, if we click Submit, we see this:
Validation with Attributes
Another way to add the rules is to use PHP 8 syntax of attributes on top of each property variable.
Then, you don't need to pass any parameters to $this->validate()
. It will apply the rules to all the public properties with the #[Validate]
attribute.
app/Livewire/CompanyCreate.php:
use Livewire\Attributes\Validate; class CompanyCreate extends Component{ // ... #[Validate('required|min:3')] public string $name = ''; #[Validate('required')] public string $country = ''; #[Validate('required')] public string $city = ''; // ... public function save(): void { $this->validate(); Company::create([ // ...}
The result is almost the same: you see the validation error when calling $this->validate()
.
However, there's a slight difference.
By default, this approach automatically calls the validation on every server request. This means that when we change the Country dropdown value, there's a server request to get the cities, and we have this:
/uploads/2025/03/livewire-validation-attribute-update.png
However, you can turn off this behavior by providing another attribute parameter, onUpdate: false
.
#[Validate('required|min:3', onUpdate: false)]public string $name = '';
In this case, the validation would fire only when $this->validate()
is called.
"Live" Validation
Do you remember the wire:model.live
syntax on the Country dropdown to automatically fetch the cities when the value changes?
We can apply the same live
behavior on any input to automatically call the validation, too.
For example, let's add it to the Company Name field.
Component class:
#[Validate('required|min:3')] // no "onUpdate: false"public string $name = '';
Blade:
<input wire:model.live="name" ...
Then, as you start typing the name, you see the immediate validation.
Sometimes, this behavior is useful, but be aware that this hits the server every time.
A "middle ground" between the two ways mentioned above is to update when a user removes focus from the input. For this, we need to add .blur
to the wire:model
directive.
<input wire:model.blur="name" ...
Now, try writing a few letters in the name and removing the focus from it. The validation will be shown only then.
Here's a GitHub commit for this lesson.
Form Objects
I will also mention one feature released in Livewire v3: extracting the form properties into a separate Form Object to separate the logic from the main Livewire component.
So, instead of providing all the properties like $name
, $country
, and others in the Livewire component, you create a separate form:
php artisan livewire:form CompanyForm
Then, inside that form class, you specify all the properties with their validation rules:
app/Livewire/Forms/CompanyForm.php
namespace App\Livewire\Forms; use Livewire\Attributes\Validate;use Livewire\Form; class CompanyForm extends Form{ #[Validate('required|min:3')] public string $name = ''; #[Validate('required')] public string $country = ''; #[Validate('required')] public string $city = '';}
Then, in your main Livewire component, you use that form as a property and work with the $this->form
object.
app/Livewire/CompanyCreate.php:
use App\Livewire\Forms\CompanyForm; class CreatePost extends Component{ public CompanyForm $form; // ... public function save() { // If your DB field names are the same as form properties Company::create( $this->form->all() ); }}
Also, then, in the Blade file, you work with wire:model="form.xxxxx"
syntax:
resources/views/livewire/company-create.blade.php:
<input wire:model="form.name" ...>@error('form.name') <span class="mt-2 text-sm text-red-600">{{ $message }}</span>@enderror
As a result, your main component becomes shorter. Also, you may potentially re-use the same Form Object for both Create and Edit forms. However, in our practice, we tried Form Objects in a few projects, and they haven't proved to be a good fit. Still, you may want to go that route; it's one of the options for structuring Livewire components.
In the next lesson, I will show you the Edit form and how to pass parameters to its Livewire component.
No comments yet…