skip to content

Why do Livewire 4's docs discourage $wire.$entangle() for sharing state with Alpine, and what should you use instead?

level: middleimportance: should knowfreq 25%

answer

  1. two copies of one value
  2. deferred by default, .live to sync
  3. @entangle directive deprecated
  4. read and write $wire.prop directly
  5. x-data for UI-only state

basics

~20 s

$wire.$entangle() creates a second Alpine copy of a Livewire property and keeps the two in sync, which duplicates state and causes ordering and performance problems. Livewire 4 keeps it only for compatibility; read and write $wire.prop directly instead.

solid answer

~40 s

`$wire.$entangle('showCalendar')` returns a value Alpine stores in its own `x-data`, then links that copy to the Livewire property in both directions; changes are deferred to the next request unless you add `.live`. That means two sources of truth, sync effects to maintain, and surprises when the element is removed or the server and client change the value in the same tick. Livewire 4's docs keep it for backwards compatibility, label it deprecated in the `$wire` reference, and warn against the `@entangle` Blade directive outright. The replacement is simpler: Alpine expressions read and assign `$wire.showCalendar` directly, which is already reactive; state PHP never needs stays in plain `x-data`.

go deeper

for a junior

Know that in Livewire 4 you use $wire.prop directly from Alpine, and that $entangle is an older pattern you may see in existing code.

for a middle

Explain what entangling does, its deferred default and .live option, and why two synchronized copies are harder to reason about than one.

for a senior

Migrate entangled components: move UI-only state to x-data, replace the rest with $wire reads, writes and $watch, and check request counts afterwards.

for a principal

Plan how to retire deprecated patterns across a codebase and third-party components during a Livewire upgrade without blocking feature work.

## What entangling does **Entangling** links an Alpine property to a Livewire property so that each updates the other: ```html <div x-data="{ open: $wire.$entangle('showCalendar') }"> <button x-on:click="open = true">Pick dates</button> <div x-show="open">...</div> </div> ``` Under the hood, `$entangle(name, live = false)` reads the current Livewire value, clones it into Alpine's `open`, and registers a two-way binding: when Alpine changes `open`, Livewire's client state for `showCalendar` is set; when a response changes `showCalendar`, Alpine's `open` is updated. By default the write to Livewire is **deferred** until the next request; `$wire.$entangle('showCalendar').live`, or passing `true` as the second argument, sends a request on every change. The `@entangle('showCalendar')` Blade directive compiles to the same call against `window.Livewire.find(id)`. ## Why Livewire 4 discourages it The Livewire 4 docs say you "probably don't need this": entangling "creates duplicate state that can cause predictability and performance issues", the API is "maintained for backwards compatibility but is discouraged for new code", and the `@entangle` directive "has been deprecated and causes issues when removing DOM elements". The `$wire` reference lists `$entangle` as deprecated. The problems follow from having **two copies**: - **Two sources of truth.** Alpine's `open` and Livewire's `showCalendar` can disagree between a local change and the next sync, and code must know which one it is reading. - **Object cloning.** Arrays and objects are deep-cloned into Alpine, so nested changes go through the sync machinery rather than one shared object. - **Lifecycle edges.** When the element holding the entangled `x-data` is removed or re-created during a morph, the binding has to be torn down cleanly; the Blade directive in particular is flagged for trouble here. - **Hidden request cost.** A `.live` entangle turns an innocent Alpine toggle into a network request per change. ## What to use instead In Livewire 4, `$wire` is itself reactive inside Alpine, so there is no need for a copy: | Need | Livewire 4 idiom | |---|---| | Show something from PHP state | `x-show="$wire.showCalendar"` | | Change it locally, sync later | `x-on:click="$wire.showCalendar = true"` | | Change it and tell PHP now | `$wire.$set('showCalendar', true)` or an action | | Pure UI state PHP never needs | `x-data="{ open: false }"`, no Livewire property at all | | React in JavaScript when it changes | `$wire.$watch('showCalendar', fn)` | ## Migrating an entangled component 1. Ask whether PHP actually needs the value. A calendar popover's open state usually does not: delete the Livewire property and keep it in `x-data`. 2. If PHP does need it, replace `open` with `$wire.showCalendar` in every Alpine expression and remove the entangle. 3. Where the old code used `.live`, decide whether a request per change is really wanted; if so, use `$set` or `wire:model.live` on a real input. 4. Replace `@entangle(...)` in Blade with the `$wire` form; it is the deprecated path. ## Before and after ```html <!-- Livewire 3 style: a second copy of the state --> <div x-data="{ nights: $wire.$entangle('nights') }"> <button x-on:click="nights++">+1 night</button> <span x-text="nights"></span> </div> <!-- Livewire 4 style: one copy, read and written through $wire --> <div> <button x-on:click="$wire.nights++">+1 night</button> <span x-text="$wire.nights"></span> </div> ``` Both versions update the number on screen instantly and send the new value with the next request. The second has no binding to set up or tear down, no clone of the value, and nothing that can drift out of sync. ## When it still appears You will meet `$wire.$entangle` and `@entangle` in Livewire 2 and 3 code, and in third-party Blade components written for them. They still work in Livewire 4, which is why an upgrade does not break, but new code should not add more.

  • What does $wire.$entangle('guests').live change compared with the default?
    By default an entangled value writes to Livewire's client state without a request, so PHP sees the change with the next request. `.live`, or passing `true` as the second argument, makes each Alpine change set the property and send a request at once. That is also the easiest way to generate far more requests than intended.
  • Does removing $entangle from a Livewire component mean Alpine can no longer react to server changes?
    No. `$wire.prop` is reactive inside Alpine expressions, so `x-show="$wire.showCalendar"` re-evaluates when a response changes it. For imperative code, `$wire.$watch('showCalendar', callback)` runs whenever the value changes, without keeping a second copy.

saying these in an interview costs you the question

  • $wire.$entangle is the recommended way to share Livewire state with Alpine.
  • Entangled values send a request on every change by default.
  • Without entangle, Alpine cannot react to Livewire property changes.
  • The @entangle Blade directive is the preferred form in Livewire 4.
  • Livewire 4 removed $entangle, so old code breaks after upgrading.