The problem: Updating a bound input with wire:model changes the underlying property, but the UI doesn't visually update until an unrelated action triggers a re-render.
Environment: Laravel 11, Livewire 3.x, PHP 8.3, Alpine.js 3.x (bundled)
The Error
No JS error thrown.
Network tab: POST /livewire/update → 200 OK, payload updated correctly.
DOM: input value changes, but dependent <span>{{ $total }}</span> stays stale
until page is manually refreshed or another wire:click fires.
Why It Happens
This is one of the more misleading Livewire bugs because the network request actually succeeds — the server receives the updated property and even computes the right value. The UI just doesn't reflect it. Three causes account for almost every case:
1. Missing wire:key on a looped or conditionally-rendered element. Livewire's DOM diffing algorithm uses wire:key to track which elements map to which data. Without it, Livewire can't reliably tell the difference between "this element changed" and "this element is new," so it sometimes skips the visual patch entirely.
2. A computed property or method isn't reactive. If $total is calculated inside a regular PHP method called directly in the Blade view ({{ $this->calculateTotal() }}) rather than a Livewire #[Computed] property, Livewire's front-end morph step may not know it needs to re-evaluate that expression on every render.
3. wire:model vs wire:model.live confusion. As of Livewire 3, plain wire:model is deferred by default — it only syncs on the next network request (like a button click), not on every keystroke. If you expect live updates as the user types, you need wire:model.live explicitly.
The Fix
First, check whether you actually want live syncing or deferred syncing, and update the directive accordingly:
- <input type="number" wire:model="quantity">
+ <input type="number" wire:model.live="quantity">
Next, make sure any looped elements have a stable, unique wire:key:
@foreach ($items as $item)
<div wire:key="item-{{ $item->id }}">
{{ $item->name }}
</div>
@endforeach
Finally, convert any manually-called total/calculation method into a proper computed property so Livewire tracks it correctly:
use Livewire\Attributes\Computed;
#[Computed]
public function total()
{
return $this->quantity * $this->price;
}
Then reference it in Blade with the property-style call, not a method call:
<span>{{ $this->total }}</span>
Step-by-Step
- Decide if the field needs live (per-keystroke) or deferred (on-action) syncing, and set
wire:model.liveor plainwire:modelaccordingly. - Add
wire:keyto every element inside a@foreachloop, using a stable unique value like the record's ID. - Replace any direct method calls used for derived values with a
#[Computed]property. - Update the Blade view to reference computed properties as
$this->propertyName, not$this->methodName(). - Hard refresh and retest the interaction — type into the field and confirm the dependent element updates without needing another click.
- Check the Network tab to confirm only one request fires per intended update, not one per keystroke if you meant to use deferred binding.
Edge case: If the component is nested inside a parent Livewire component, a missing wire:key on the child component tag itself (not just loop items inside it) can cause the entire child to silently fail to re-render on parent updates — this is easy to miss because the child's own internal state changes work fine, it's specifically parent-triggered updates that get skipped.
Still not updating after all this? Reach out on the Contact page and I'll help debug your specific case.