skip to content

In Livewire 4, how do the wire:model modifiers .blur, .change, .enter and .deep behave, and why does .live.blur differ from .blur.live?

level: middleimportance: should knowfreq 42%

answer

  1. before .live: client-side sync
  2. after .live: network timing
  3. .blur alone sends no request
  4. .lazy kept for v3 compatibility
  5. .deep hears bubbling child events

basics

~20 s

In Livewire 4, modifiers before .live set when client-side state syncs; modifiers after .live set when the request is sent. .blur.live syncs and sends on blur; .live.blur syncs per keystroke, sends on blur. .deep hears child events again.

solid answer

~40 s

Livewire 4 splits `wire:model` modifiers at `.live`. Modifiers **before** `.live` (or with no `.live`) control when the input's value is written to the component's client-side state; modifiers **after** it control when the request is sent. So `wire:model.blur` updates `$wire.title` only on blur and sends nothing; `wire:model.blur.live` updates and sends on blur; `wire:model.live.blur` updates client state on every keystroke but sends only on blur, which is Livewire 3's `.blur`. `.change` and `.enter` work the same way for the change event and the Enter key. `wire:model.lazy` survives as a v3-compatible alias for sending on change. `.deep` restores listening to events bubbling from child elements, because Livewire 4's `wire:model` hears only events from its own element by default.

code

html · 11 lines
html
<div>
    <input type="search" wire:model.enter.live="search">

    <input type="number" wire:model.live.blur="minPrice">
    <input type="number" wire:model.live.blur="maxPrice">

    <select wire:model.live.change="category">
        <option value="">All</option>
        <option value="audio">Audio</option>
    </select>
</div>

go deeper

for a junior

Recall what .blur, .change, .enter and .deep do, and that .live is needed for any of them to send a request.

for a middle

Explain the split at .live between client-side sync and network timing, and why .blur.live and .live.blur differ.

for a senior

Plan a Livewire 3 to 4 upgrade of bindings, catching .blur fields that silently stopped sending and containers that relied on bubbling.

for a principal

Weigh per-field sync timing against user feedback and server load when setting form conventions for many teams.

## Two clocks in one binding A `wire:model` binding has two moments that can be controlled separately: 1. **Client-side sync**: when the input's value is written into the component's JavaScript state (`$wire.title`), which is what `wire:dirty`, Alpine expressions and `$wire` reads see. 2. **Network sync**: when that state is sent to the server in a request. In Livewire 3, typing always updated client-side state immediately, and modifiers like `.blur` only chose when the request went out. **Livewire 4** (the timing change arrived in 4.1) lets modifiers control **both** clocks, split at `.live`. ## The rule: split at .live - Modifiers **before** `.live`, or on a binding without `.live`, control **client-side sync**. - Modifiers **after** `.live` control **when the request is sent**. | Binding | Client state updates | Request sent | |---|---|---| | `wire:model.blur` | On blur | Never by itself, rides on the next request | | `wire:model.blur.live` | On blur | On blur | | `wire:model.live.blur` | On every input | On blur (the Livewire 3 `.blur` behaviour) | | `wire:model.change` | On change | Never by itself | | `wire:model.live.change` | On every input | On change | | `wire:model.enter` | On Enter | Never by itself | | `wire:model.enter.live` | On Enter | On Enter | | `wire:model.lazy` | On change | On change (kept for v3 compatibility) | With `.live.blur`, the blur request is skipped when nothing changed since the last server sync. ## What each modifier is for - **`.blur`**: text fields where you want validation or saving only when the user leaves the field. A **price** input in a product search is a typical case: no request per digit. - **`.change`**: selects, where the change event fires once per choice. - **`.enter`**: search boxes that should apply only when the user presses Enter. - **`.deep`**: see below. ## Upgrading from Livewire 3 The upgrade guide's mapping: 1. `wire:model.blur` in v3 becomes `wire:model.live.blur` in v4. 2. `wire:model.change` in v3 becomes `wire:model.live.change` in v4. 3. `wire:model.lazy` keeps working unchanged. Leaving v3 `.blur` bindings untouched after an upgrade silently changes behaviour: the request that used to fire on blur no longer happens, so real-time validation on blur stops until the next action. ## .deep and event propagation In Livewire 4, `wire:model` listens only to input and change events that **originate on its own element**, as if Alpine's `.self` were applied. That prevents a `wire:model="open"` on a modal container from reacting when an input inside the modal is cleared. When you genuinely want a container to collect events from its descendants, use `.deep`: ```html <div wire:model.deep="price"> <input type="range" min="0" max="500"> </div> ``` In Livewire 3 the bubbling behaviour was the default; Livewire 4 made it opt-in. ## Seeing the difference in practice The two clocks matter whenever something on the page reads client-side state before the server does: - A character counter written with Alpine reading `$wire.title` updates per keystroke with `.live.blur`, but only after blur with `.blur.live`. - `wire:dirty` compares client-side state with the server's. With `.live.blur`, the field turns dirty as soon as the user types and clean after the blur request; with `.blur` alone it turns dirty on blur and stays dirty until the next request. - Form submits still carry whatever client-side state exists at that moment, so a field bound with `.enter` that the user never confirmed with Enter is submitted with its **old** value. ## Combining with debounce `.debounce` and `.throttle` belong after `.live` as well: `wire:model.live.debounce.400ms="search"`. They have no effect on client-side sync.

  • After upgrading to Livewire 4, blur-triggered validation on wire:model.blur fields stopped working; why?
    In Livewire 4, modifiers without `.live` only control client-side sync, so `wire:model.blur` updates `$wire` on blur but sends no request. The v3 behaviour is now spelled `wire:model.live.blur`, which sends the request on blur and lets `#[Validate]` rules run then.
  • In Livewire 4, why doesn't wire:model on a modal wrapper react when an input inside it changes?
    Livewire 4's `wire:model` only listens for events whose target is its own element, so events bubbling up from a child input are ignored. Add `.deep` to restore the Livewire 3 behaviour of collecting events from descendants.

saying these in an interview costs you the question

  • wire:model.blur and wire:model.live.blur are the same in Livewire 4
  • Modifier order after wire:model never matters
  • wire:model.lazy was removed in Livewire 4
  • wire:model on a container hears child input events by default in Livewire 4
  • After an upgrade, v3 .blur bindings keep sending requests on blur unchanged