---
title: "Derived State With computed() in LemonadeJS"
description: "Derived values that stay live: what computed() is, auto-tracking mechanics, two production bugs it eliminates, and disposal semantics."
source: https://lemonadejs.com/docs/computed/
---

# Derived state with computed()

`computed()` returns a derived value that stays live. The function
re-evaluates whenever any
state read inside it changes, and the result is itself a readable state:

```javascript
const C = (props, { state, computed }) => {
    const qty = state(2);
    const total = computed(() => qty.value * (props.price?.value ?? 0));
    return html`<b>${total}</b>`;   // re-renders when qty OR props.price change
};
```

Write the sources, never the result. The derivation is disposed with the
component; a dead component never computes again.

A derived state follows its sources: change either input and the total and the shipping line update.

<!--example-->

```js
import { html } from 'lemonadejs';

const App = (props, { state, computed }) => {
    const qty = state(2);
    const price = state(19.9);
    const total = computed(() => (qty.value * price.value).toFixed(2));
    const shipping = computed(() => (total.value > 100 ? 'free shipping' : 'shipping 4.90'));

    return html`<div>
        <label>Quantity <input type="number" min="1" bind="${qty}" /></label>
        <label>Unit price <input type="number" step="0.1" bind="${price}" /></label>
        <p>Total: <b>${total}</b> · ${shipping}</p>
    </div>`;
};
```

## The problem

Two real bug classes motivated `computed()`. Both were found while
building the Studio catalog, not imagined.

**The captured-once snapshot.** A component receives a live prop and
derives something from it during setup:

```javascript
// BUG: reads the prop ONCE, at construction
const position = { top: props.top.value + 10, left: props.left.value };
```

Setup runs once, and that is the whole point of the architecture, so this
derivation is frozen forever. The Modal block shipped exactly this bug:
panels fed live anchor coordinates opened at `0,0` because the coordinates
were read before the caller measured them.

**The hand-rolled pipeline.** Without a derivation primitive, components
invent one: a `version` counter state, bumped from subscriptions, that
render bindings read to know when to recompute. The gantt block's first
version did this, and hit a second trap inside it: `version.value++`
*reads* the state it writes, which inside a tracked context subscribed the
callback to itself (`LJS-203: update loop detected`).

## Mechanics

`computed()` makes the live derivation the *easy* path:

```javascript
// FIXED: a derived state that follows its sources
const position = computed(() => ({
    top: props.top.value + 10,
    left: props.left.value,
}));
// position.value is always current; ${position} in a template is live
```

Mechanics worth knowing:

- **Eager and synchronous.** The function runs immediately and on every
  source change; after a source assignment returns, the computed value is
  current. No scheduling model to reason about.
- **Auto-tracked.** Whatever states the function reads (local states,
  props, stores) become its dependencies. Dependencies re-track on every
  run, so conditional reads work (`a.value ? b.value : c.value` tracks `b`
  only while `a` is true).
- **Invisible to the snapshot heuristic.** The initial evaluation runs
  during setup, but its reads are *binding* reads, not
  template-construction reads, so they never count toward the dev
  `LJS-202` snapshot warning, so using `computed()` correctly cannot
  trip the very warning it exists to prevent.
- **Instance-scoped.** The derivation is registered with the component and
  disposed on unmount. The test suite pins this: after `unmount()`, source
  changes no longer execute the function.
- **A state like any other.** Render it (`${total}`), pass it as a prop
  (it stays live across the boundary), `subscribe()` to it, `peek()` it.

Related primitive: `subscribe(cb)` runs callbacks **untracked** with
addEventListener semantics, pinned to its one source state. That engine
guarantee is what makes the gantt's `version.value++` safe today; the
component that exposed the loop now runs the exact same line, protected by
the engine instead of working around it.

## In practice

**Live panel anchoring** (the bug that motivated the feature, fixed):

```javascript
const Anchored = (props, { computed }) => {
    const top = computed(() => (props.anchor?.value?.bottom ?? 0) + 1);
    const left = computed(() => props.anchor?.value?.left ?? 0);
    return html`<${Modal} position="absolute" top="${top}" left="${left}" header="${false}">
        ${props.children}
    </${Modal}>`;
};
```

**A filtered count beside a list**, derived from the same array the list
mutates in place:

```javascript
const Inbox = (props, { state, computed }) => {
    const items = state([{ unread: true }, { unread: false }]);
    const unread = computed(() => items.value.filter((i) => i.unread).length);

    const readAll = () => {
        items.value.forEach((i) => (i.unread = false));   // mutate in place
        items.touch();                                    // notify once
    };

    return html`<div>
        <h3>Inbox <small>(${unread} unread)</small></h3>
        <button onclick="${readAll}">Mark all read</button>
    </div>`;
};
```

`touch()` notifies the computed, the computed notifies the heading: one
mutation pass, no clones, and the derived count can never be stale.

## Reference

```javascript
const C = (props, { computed }) => {
    const derived = computed(fn);   // State<ReturnType<fn>>
};
```

- `fn` runs immediately and on every change of any state it reads.
- The result is read-only by convention: write the sources.
- Disposed with the component instance.
- For module-scope derivations, derive at use sites from `store()` values
  or wrap a component around the derivation; `computed` is a component
  tool by design; disposal needs an owner.