skip to content

In a Livewire component, what does the #[Computed] attribute do, and when does the method behind it run again?

level: middleimportance: must knowfreq 55%

answer

  1. use Livewire\Attributes\Computed
  2. read as $this->name in Blade
  3. memoized for one request only
  4. unset($this->name) busts it
  5. persist: true, 3600 seconds default

basics

~20 s

#[Computed] turns a Livewire method into a property memoized for one request: the first $this->name read runs it, later reads reuse the result. Each new request runs it again on first read; unset() busts it, persist: true caches across requests.

solid answer

~40 s

`#[Computed]` (from `Livewire\Attributes\Computed`) makes a method readable as a property, `$this->lowStock`, in the class and in Blade. The first access in a request calls the method; later accesses in that **same request** return the memoized value. The value is **not** part of the snapshot, so each later request, triggered by any action or update, runs the method again on first access, and only if something reads it. `unset($this->lowStock)` clears the memo when you changed the data it depends on. For cross-request caching, `#[Computed(persist: true)]` caches per component instance in Laravel's cache for `seconds` (3600 by default), and `cache: true` shares one value across all instances. Computed methods cannot be called as actions from the browser and are not supported on form objects.

code

html · 11 lines
html
<div>
    @if ($showList)
        @foreach ($this->lowStock as $product)
            <div wire:key="p-{{ $product->id }}">
                {{ $product->sku }}: {{ $product->quantity }}
            </div>
        @endforeach
    @endif

    <span>{{ $this->lowStock->count() }} items low</span>
</div>

go deeper

for a junior

Recall the attribute's import, the $this-> access in Blade, and that the value is reused within one request only.

for a middle

Explain the per-request memo, when the method re-runs, unset() busting, and how persist and cache differ from memoization.

for a senior

Use computed properties to keep queries out of the snapshot and render(), and choose persist or cache with a key and expiry you can reason about.

for a principal

Decide team rules for derived data in components, balancing query cost per request against cache staleness and invalidation paths.

## What a computed property is A **computed property** in Livewire is a method marked with the `#[Computed]` attribute (import `Livewire\Attributes\Computed`). You read it like a property, `$this->lowStock`, and Livewire calls the method behind it. It is **derived** data: it is calculated from public properties, the database or the current user, and it is **not stored** in the component's snapshot. ```php use App\Models\Product; use Livewire\Attributes\Computed; public int $warehouseId; public int $threshold = 10; #[Computed] public function lowStock() { return Product::where('warehouse_id', $this->warehouseId) ->where('quantity', '<', $this->threshold) ->orderBy('quantity') ->get(); } ``` In the template it **must** be read through `$this`: `@foreach ($this->lowStock as $product)`. A bare `$lowStock` is undefined, because only public properties and data passed from `render()` become view variables. ## When the method runs - **First access in a request**: the method runs and its return value is **memoized**. - **Later accesses in the same request**: the memoized value is returned; the query does not run again. - **Never accessed**: the method does not run at all, so a computed value used inside an `@if` costs nothing when the branch is false. - **Next request** (a click, a property update, any round trip): the memo is gone, so the first access runs the method again. Developers often assume the value lives for the lifetime of the component on the page. It does not: **memoization lasts one request**. ## Busting the memo If an action changes the data a computed property depends on after it was read, the memo is stale for the rest of that request. `unset($this->lowStock);` clears it, and the next access recomputes: ```php public function restock(int $productId): void { Product::whereKey($productId)->increment('quantity', 50); unset($this->lowStock); } ``` ## Caching across requests Memoization is not caching. When the value is expensive and may be reused across requests, the attribute takes parameters backed by Laravel's cache: | Parameter | Default | Effect | |---|---|---| | `persist` | `false` | Cache per component instance across requests (key built from the component id) | | `seconds` | `3600` | Lifetime of the cached value | | `cache` | `false` | Share one cached value across all instances of the component | | `key` | `null` | Custom cache key, useful for clearing it from elsewhere | | `tags` | `null` | Cache tags, used only when the store supports tags | `unset()` on a persisted or cached computed property clears both the in-request memo and the cached entry. ## Limits worth knowing 1. A computed method is **not an action**: a browser call such as `wire:click="lowStock"` throws `CannotCallComputedDirectlyException`. 2. Computed properties are **not supported on `Livewire\Form` objects**. 3. The method name is exposed as a camelCase property name, so `low_stock()` is read as `$this->lowStock`. 4. Because nothing is stored in the snapshot, computed values never add to the payload and cannot be tampered with from the browser. ## Why not just call the method? You could write `$this->lowStock()` everywhere instead. The difference is the memo: a template that checks `$this->lowStock->isEmpty()`, loops over it, and prints its count would run the query three times with plain method calls, and once with a computed property. The attribute also keeps call sites free of parentheses, so a value can move between a public property and a computed property without rewriting the template. ## Computed versus render() data and public properties | Where the data comes from | Runs | In the snapshot | |---|---|---| | Data passed from `render()` | Every re-render, whether used or not | No | | Public property assigned in `mount()` | Once, then restored from the snapshot | Yes | | `#[Computed]` method | Once per request, only if read | No | For a warehouse dashboard widget that lists low-stock products, a computed property keeps the query with its constraints and eager loads intact, runs it at most once per request, and skips it when the list is hidden.

  • In Livewire, what is the difference between #[Computed(persist: true)] and #[Computed(cache: true)]?
    `persist: true` caches the value per component instance, keyed on that instance's id, so each widget on the page has its own cached value across its requests. `cache: true` keys on the component name, so every instance of the component in the application shares one cached value until it expires or is cleared. Both default to 3600 seconds.
  • Why does wire:click="lowStock" fail when lowStock() is marked #[Computed]?
    Livewire refuses to call a computed method as an action and throws `CannotCallComputedDirectlyException`. Computed methods are read-only derived values; if the browser needs to trigger a recalculation, call a real action that changes state or unsets the computed property.
  • When is passing data from render() better than a Livewire computed property?
    When the data is cheap, always used, and should be fresh on every render anyway. `render()` runs on every re-render regardless, so for expensive, conditional or repeatedly read values a computed property is better because it runs at most once and only when read.

A computed property is like a whiteboard note a warehouse clerk writes during one shift: every question during that shift is answered from the note, but the next shift wipes the board and recounts on the first question asked.

saying these in an interview costs you the question

  • A computed value is cached for the life of the component on the page
  • Computed properties are stored in the snapshot and sent to the browser
  • In Blade you read a computed property as a plain $name variable
  • A computed method runs on every request even if nothing reads it
  • You can call a computed method as an action with wire:click