skip to content

In a Livewire component, how do #[Validate] attributes and $this->validate() work together, and how does real-time validation happen?

level: middleimportance: must knowfreq 55%

answer

  1. use Livewire\Attributes\Validate
  2. runs on every update of that property
  3. .live or .blur makes it real-time
  4. still call $this->validate() before saving
  5. rules() runs only when validate() is called

basics

~20 s

#[Validate('required|numeric')] on a Livewire public property registers its rules and validates that property whenever an update for it reaches the server, so .live or .live.blur bindings give real-time errors. $this->validate() checks every rule before saving and throws on failure.

solid answer

~40 s

`#[Validate('required|numeric|min:0')]` (from `Livewire\Attributes\Validate`) does two things: it registers the rule for `validate()`, and by default it runs `validateOnly()` for that property **every time an update for it reaches the server**. Combined with `wire:model.live` or `.live.blur`, that is **real-time validation**: the error appears as the user leaves the field. You still call `$this->validate()` in the action, because fields the user never touched were never updated and so never validated; it validates all rules, returns the validated data and throws a `ValidationException`, which Livewire turns into the component's error bag. A `rules()` method is needed for rule objects built at runtime; its rules run only on `validate()` unless a bare `#[Validate]` marks the property for update-time validation. `onUpdate: false` turns update-time checks off.

code

php · 31 lines
php
<?php

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

new class extends Component {
    #[Validate('nullable|string|max:100')]
    public $search = '';

    #[Validate('nullable|numeric|min:0', as: 'minimum price')]
    public $minPrice = null;

    public array $results = [];

    public function applyFilters(): void
    {
        $data = $this->validate();

        $this->results = Product::query()
            ->where('price', '>=', $data['minPrice'] ?? 0)
            ->pluck('name')
            ->all();
    }
};
?>

<form wire:submit="applyFilters">
    <input wire:model.live.blur="minPrice" type="number">
    <button type="submit">Apply</button>
</form>

go deeper

for a junior

Recall the #[Validate] attribute on a property, that validate() must be called before saving, and that errors appear in the view.

for a middle

Explain update-time validation, how .live or .live.blur make it real-time, and why untouched fields need validate().

for a senior

Choose between attributes and rules() for runtime rules, use bare #[Validate] and onUpdate: false deliberately, and keep actions safe on failure.

for a principal

Set validation conventions for components and form objects so rule placement and real-time feedback stay consistent across a codebase.

## Two ways to declare rules Livewire validates **public properties** with Laravel's validator. Rules can be declared in two places: | Where | Syntax | Runs automatically on update? | |---|---|---| | `#[Validate]` attribute | `#[Validate('required\|numeric\|min:0')]` above the property | Yes, by default | | `rules()` method | `protected function rules(): array` returning the rule array | No, only when `validate()` runs | The attribute lives in `Livewire\Attributes\Validate`. It is repeatable, accepts an array of rules, and takes options such as `as:` (the attribute name shown in messages), `message:` (a custom message) and `onUpdate:`. ## What happens on an update When a request brings a new value for a property that has `#[Validate]` rules, Livewire assigns the value and then runs `validateOnly()` for that property. If it fails, the component's error bag receives the messages and the view can show them straight away. This is what makes **real-time validation** a matter of binding timing, not extra code: - `wire:model.live="minPrice"` validates on every debounced change. - `wire:model.live.blur="minPrice"` validates when the user leaves the field. - Plain `wire:model` validates the property only when its deferred update arrives, which is usually together with the submit. ## Why you still call validate() ```php public function applyFilters(): void { $validated = $this->validate(); // $validated contains only the validated fields } ``` Real-time checks only cover properties that were **updated**. A required field the user never touched never produced an update and was never checked. `$this->validate()`: 1. Runs **all** rules, from attributes and from `rules()`, including rules on form objects. 2. Throws `Illuminate\Validation\ValidationException` on failure. Livewire catches it, fills the component's error bag and stops the action there, so code after `validate()` does not run. 3. Returns the **validated data** on success, like a controller's `$request->validate()`. 4. Clears previous errors when it passes. For a single field, `$this->validateOnly('minPrice')` checks just that property. ## When rules() is the right place Attributes need constant expressions, so rules that depend on runtime data, such as `Rule::unique('products')->ignore($this->product)` or a `max` that depends on another property, go in `rules()`. Those rules run only when `validate()` is called. To keep real-time validation for such a property, add a bare `#[Validate]` (no rule) above it; the property is then validated on update using the rule from `rules()`. ## Turning update-time validation off `#[Validate('required|numeric', onUpdate: false)]` keeps the rule for `validate()` but stops it from running on each update. That suits fields where early errors annoy users, such as a field they are expected to fill last. ## A price-range example A product search validates its range in real time: ```php use Livewire\Attributes\Validate; #[Validate('nullable|numeric|min:0')] public $minPrice = null; #[Validate('nullable|numeric|gte:minPrice')] public $maxPrice = null; ``` With `wire:model.live.blur` on both inputs, a negative minimum shows an error when the user tabs away; `applyFilters()` still calls `$this->validate()` before querying. ## Common mistakes - Relying on real-time validation alone and skipping `validate()` in the action. - Expecting `rules()` rules to run on update without a bare `#[Validate]`. - Forgetting that `.blur` without `.live` in Livewire 4 sends nothing, so no real-time error appears.

  • In Livewire, what happens to the rest of an action when $this->validate() fails?
    `validate()` throws a `ValidationException`. Livewire catches it, puts the messages in the component's error bag and re-renders, but the remaining code in the action does not run. That is why saving code can safely follow the `validate()` call.
  • How do you validate a Livewire property in real time when its rule needs Rule::unique()->ignore($this->product)?
    Declare the rule in a `rules()` method, since attribute arguments must be constant expressions, and put a bare `#[Validate]` without a rule above the property. The bare attribute marks it for validation on update, using the rule from `rules()`.

saying these in an interview costs you the question

  • #[Validate] rules only run when $this->validate() is called
  • With real-time validation you can skip $this->validate() in the action
  • Rules in rules() run automatically on every property update
  • Code after a failed $this->validate() still runs
  • Plain deferred wire:model gives errors as the user types