Muhammad Yasir Amin

Loading websiteโ€ฆ

More from Muhammad Yasir Amin

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Subscribe on YouTube

Laravel 13 Livewire Form Validation Tutorial: Validate Attribute, Form Objects and Real-Time Errors

๐Ÿ› ๏ธ Laravel Tutorials5 min read

Laravel 13 Livewire Form Validation Tutorial: Validate Attribute, Form Objects and Real-Time Errors

Muhammad Yasir Amin

Muhammad Yasir Amin

Senior Full Stack Engineerโ€ขNov 12, 2026
Laravel 13 Livewire Form Validation Tutorial: Validate Attribute, Form Objects and Real-Time Errors

To validate a form in Livewire on Laravel 13, add #[Validate] attributes with Laravel rules to your component properties, call $this->validate() before saving, and show errors with Blade's @error directive. For larger forms, move the fields and rules into a Livewire\Form object; for instant feedback, bind inputs with wire:model.live.blur.

Livewire validation uses the same rule strings and rule objects as normal Laravel validation, so everything you know about form requests carries over.

Prerequisites

  • Laravel 13 with PHP 8.3 or newer.
  • Livewire 4, installed with composer require livewire/livewire.
  • A layout that loads Livewire's scripts (Livewire injects them automatically on pages that use components).

Step 1: Create a class-based component

Livewire 4 creates single-file components by default. This tutorial uses a class-based component because it keeps PHP and Blade apart, which is easier to follow:

Code
php artisan make:livewire ContactForm --class

That creates app/Livewire/ContactForm.php and resources/views/livewire/contact-form.blade.php. The validation code is identical in single-file components.

Step 2: Add rules with the Validate attribute

JavaScript
<?php

namespace App\Livewire;

use App\Models\ContactMessage;
use Livewire\Attributes\Validate;
use Livewire\Component;

class ContactForm extends Component
{
    #[Validate('required|string|min:2|max:100')]
    public string $name = '';

    #[Validate('required|email:rfc|max:255')]
    public string $email = '';

    #[Validate('required|string|min:10|max:2000', message: 'Please write at least a short sentence.')]
    public string $body = '';

    public bool $sent = false;

    public function save(): void
    {
        $validated = $this->validate();

        ContactMessage::create($validated);

        $this->reset('name', 'email', 'body');
        $this->sent = true;
    }

    public function render()
    {
        return view('livewire.contact-form');
    }
}

When a property bound with wire:model is updated, Livewire validates that property against its attribute. Calling $this->validate() in save() checks every rule, including fields the user never touched, and returns only the validated values. The sent property has no attribute, so it is never part of the validated data.

Step 3: Show errors in the view

Shell
<form wire:submit="save">
    @if ($sent)
        <p class="success">Thanks, your message was sent.</p>
    @endif

    <label for="name">Name</label>
    <input id="name" type="text" wire:model.live.blur="name">
    @error('name') <p class="error">{{ $message }}</p> @enderror

    <label for="email">Email</label>
    <input id="email" type="email" wire:model.live.blur="email">
    @error('email') <p class="error">{{ $message }}</p> @enderror

    <label for="body">Message</label>
    <textarea id="body" wire:model="body" rows="5"></textarea>
    @error('body') <p class="error">{{ $message }}</p> @enderror

    <button type="submit" wire:loading.attr="disabled">Send</button>
</form>

wire:model.live.blur sends the value when the field loses focus, so the user sees an error right after leaving a field instead of after submitting. The body field uses plain wire:model, so it is only checked on submit. I prefer blur over .live on every keystroke because it avoids a network request per character.

Render the component in any Blade page with <livewire:contact-form />.

When should you use rules() instead of attributes?

Attributes must contain constant values. If a rule depends on runtime data, such as a Rule::unique() that ignores the current record, define a rules() method:

JavaScript
use Illuminate\Validation\Rule;

protected function rules(): array
{
    return [
        'email' => ['required', 'email', Rule::unique('subscribers', 'email')->ignore($this->subscriberId)],
    ];
}

Rules from rules() run when you call $this->validate(). If you also want real-time validation for that property, add an empty #[Validate] attribute to it.

Step 4: Move bigger forms into a form object

Once a component has many fields, put them in a form object so the component stays readable and the form can be reused for create and edit screens.

PHP
<?php

namespace App\Livewire\Forms;

use Livewire\Attributes\Validate;
use Livewire\Form;

class ContactFormData extends Form
{
    #[Validate('required|string|min:2|max:100')]
    public string $name = '';

    #[Validate('required|email:rfc|max:255')]
    public string $email = '';

    #[Validate('required|string|min:10|max:2000')]
    public string $body = '';
}
JavaScript
public ContactFormData $form;

public function save(): void
{
    $validated = $this->form->validate();

    ContactMessage::create($validated);

    $this->form->reset();
}

In the view, prefix fields with form.: wire:model.live.blur="form.email" and @error('form.email').

Common mistakes

  • Forgetting $this->validate() in the action. Real-time validation only checks fields that changed. Without the final call, untouched required fields slip through.
  • Wrong error key with form objects. Use @error('form.email'), not @error('email').
  • Dynamic values in attributes. PHP attributes cannot call functions. Use rules().
  • Trusting public properties. Users can change any public property from the browser. Validate everything you save and lock ids with #[Locked].

How do you verify it works?

JavaScript
<?php

use App\Livewire\ContactForm;
use Livewire\Livewire;

test('the contact form requires all fields', function () {
    Livewire::test(ContactForm::class)
        ->call('save')
        ->assertHasErrors(['name' => 'required', 'email' => 'required', 'body' => 'required']);
});

test('a valid message is saved', function () {
    Livewire::test(ContactForm::class)
        ->set('name', 'Sara')
        ->set('email', 'sara@example.com')
        ->set('body', 'Hello, I would like a quote.')
        ->call('save')
        ->assertHasNoErrors()
        ->assertSet('sent', true);

    $this->assertDatabaseHas('contact_messages', ['email' => 'sara@example.com']);
});

In the browser, leave the email field with an invalid value and confirm the error appears without submitting.

How do you validate arrays and nested data?

Livewire supports Laravel's wildcard rules. For a list of email inputs stored in public array $emails = [];, add #[Validate(['emails' => 'required|array|min:1', 'emails.*' => 'required|email'])] above the property. Bind each input with wire:model.live.blur="emails.{{ $index }}" and show errors with @error('emails.' . $index). The same dot syntax works for nested arrays inside form objects, such as form.address.city.

For uploads, Livewire validates temporary files with the usual file, image and max rules before you store them, so the checks you use in controllers carry over here as well. Store the validated file with $this->photo->store('photos', 'public') once validation passes.

FAQ

Can I use a Laravel form request in Livewire?

Not directly, because there is no HTTP request per action. Put the rules in attributes, rules() or a form object instead.

How do I clear errors?

Call $this->resetValidation() for all fields or pass a field name to clear one.

How do I rename a field in error messages?

Use the as: argument, for example #[Validate('required', as: 'full name')].

References

Article Index & Topic Tags
LaravelValidationFormsLivewire

YouTube Channel

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Muhammad Yasir Amin
Available for Contracts
Author & Solutions Architect

Senior Full Stack Engineer

Independent Senior Full Stack Engineer working on Laravel, React and Next.js applications, APIs and delivery workflows.

Laravel & PHP ApplicationsReact & Next.js InterfacesREST APIs & IntegrationsDatabase Queries & DebuggingDocker Development WorkflowsClear Scope & Handover
Discuss Your Next Step

What Does Your Application Need Next?

Share your current system, the problem you want to solve and what a useful result looks like. We can identify the right starting point and agree on a practical scope.

Technical Audit

Find the Next Useful Fix

Application Development

Scoped, Reviewable Milestones

Ongoing Support

Agreed Capacity and Priorities

Share your goals, current stack and requirements before the call.