Livewire v3 Performance: Computed Properties, Dehydration Budgets, and Wire:model Lazy
#livewire #laravel #performance #frontend

Livewire v3 Performance: Computed Properties, Dehydration Budgets, and Wire:model Lazy

3 min read Mohamed Said Mohamed Said

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:

  1. Computed properties with memoisation — avoid re-querying the database on every render.
  2. Dehydration payload hygiene — keep the snapshot small by not storing what you can recompute.
  3. wire:model.lazy and wire: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: true to computed properties backed by slow or rarely-changing queries.
  • Store only primitive scalars in public properties; reconstruct complex objects via computed methods.
  • Replace wire:model with wire:model.blur or wire:model.live.debounce.Xms on search and filter inputs.
  • Profile your component payload size in browser DevTools (Network tab, livewire/update requests) before and after — the difference is immediately visible.

Found this useful?

Frequently Asked Questions

3 questions
Q01 Does a `#[Computed]` property get included in the Livewire snapshot sent to the browser?
No. Computed properties are evaluated during the render cycle and discarded afterwards. They are never serialised into the dehydration snapshot, which is why they are the preferred way to expose Eloquent query results to your Blade templates.
Q02 When should I use `wire:model.blur` versus `wire:model.live.debounce`?
`wire:model.blur` fires exactly once when the field loses focus — ideal for form fields where immediate feedback is not required. `wire:model.live.debounce.Xms` fires after the user pauses typing, making it better for live search where you want near-real-time results without hammering the server on every keystroke.
Q03 Can I manually invalidate a cached computed property?
Yes. Use `unset($this->propertyName)` inside any Livewire action. This clears both the in-memory memoised value and, if `cache: true` was set, the Laravel cache entry for that component instance.

Continue reading

More Articles

View all