The Hidden Cost of a Livewire Round-Trip
Every network request Livewire makes carries a JSON payload in both directions: the hydration snapshot coming in and the dehydration snapshot going out. On a small component this is negligible. On a component that holds a paginated Eloquent collection, a dozen reactive properties, and a nested form, the payload can balloon past 50 KB per keystroke — and that is before you count the PHP execution time to re-render the Blade template.
Three levers give you the most leverage with the least refactoring:
- Computed properties with memoisation — avoid re-querying the database on every render.
- Dehydration payload hygiene — keep the snapshot small by not storing what you can recompute.
wire:model.lazyandwire:model.blur— defer the round-trip until the user actually finishes typing.
Computed Properties and the #[Computed] Attribute
In Livewire v3, any public method decorated with #[Computed] is memoised for the lifetime of a single render cycle. Call it ten times in your Blade template; the underlying code runs once.
use Livewire\Attributes\Computed;
use Livewire\Component;
class OrderDashboard extends Component
{
public int $statusFilter = 1;
#[Computed]
public function orders()
{
return Order::where('status', $this->statusFilter)
->with('customer')
->latest()
->paginate(25);
}
public function render()
{
return view('livewire.order-dashboard');
}
}
In the Blade template you access it as $this->orders — no parentheses. Livewire calls the method once, caches the result in memory, and discards it after the response is sent. The result is never serialised into the dehydration snapshot, which is the key performance win.
Persisting Computed Results Across Requests
If the query is expensive and the underlying data changes infrequently, add a cache TTL:
#[Computed(cache: true, seconds: 60)]
public function productCategories()
{
return Category::orderBy('name')->get();
}
Livewire stores the result in the Laravel cache keyed by component ID. Invalidate it explicitly when needed:
unset($this->productCategories); // clears the memoised + cached value
Dehydration Payload Hygiene
Livewire serialises every public property into the snapshot. Storing an Eloquent collection as a public property is the fastest way to inflate your payload.
// ❌ Serialises the entire collection into the snapshot JSON
public Collection $orders;
// ✅ Recomputed from a cheap scalar on every hydration
public int $statusFilter = 1;
#[Computed]
public function orders() { /* query here */ }
For properties you genuinely need to persist, prefer primitive scalars or small arrays. If you must persist an Eloquent model, use #[Modelable] or store only the primary key and re-fetch via a computed property.
Cutting Round-Trips with wire:model.lazy and wire:model.blur
By default wire:model fires a network request on every input event — every keystroke. For a search field that triggers a database query this is expensive.
<!-- Fires on every keystroke — avoid for DB-backed searches -->
<input wire:model="search" />
<!-- Fires only when the input loses focus -->
<input wire:model.blur="search" />
<!-- Fires only when the user presses Enter or blurs (Livewire v3 alias) -->
<input wire:model.lazy="search" />
For real-time search where you want debouncing without a full round-trip on every character, combine wire:model.live.debounce.400ms:
<input wire:model.live.debounce.400ms="search" placeholder="Search orders…" />
This waits 400 ms after the user stops typing before sending the request — a significant reduction in server load on busy components.
Practical Takeaways
- Use
#[Computed]for any property derived from a database query; it is never serialised into the snapshot. - Add
cache: trueto computed properties backed by slow or rarely-changing queries. - Store only primitive scalars in public properties; reconstruct complex objects via computed methods.
- Replace
wire:modelwithwire:model.blurorwire:model.live.debounce.Xmson search and filter inputs. - Profile your component payload size in browser DevTools (Network tab,
livewire/updaterequests) before and after — the difference is immediately visible.