docs / Runtime
derived
A computed cell: recomputes only when something it reads changes, and reads like a plain value once unwrapped.
derived(fn) creates a cell whose value is computed from other cells. It is lazy and cached: nothing runs until the cell is read, and a read recomputes only when a dependency actually changed. Use it for values that are DERIVED — a total, a filtered list, a formatted string — never for values that are owned.
component Cart() {
const &[items] = track([{ name: 'Vesk', price: 20 }])
const total = derived(() => items.reduce((sum, i) => sum + i.price, 0))
<p>Total: {get(total)}</p>
}
Read a derived cell with get(...). Printing the cell itself renders [object Object] — a derived cell is a cell, not a value, and the compiler does not unwrap it in markup the way it unwraps a track() binding.
Chaining
A derived can read another derived. Dependencies are tracked through the chain, so a change at the root updates everything downstream exactly once.
component Cart() {
const &[items] = track([{ name: 'Vesk', price: 20 }])
const total = derived(() => items.reduce((sum, i) => sum + i.price, 0))
const withTax = derived(() => get(total) * 1.2)
<p>Total: {get(total)} — with tax: {get(withTax)}</p>
}
With object and array members
component TaskList() {
const &[tasks] = track([
{ id: 1, title: 'Write docs', done: false },
{ id: 2, title: 'Ship it', done: false },
])
const open = derived(() => tasks.filter((t) => !t.done))
<ul>
for (const task of get(open)) {
<li>{task.title}</li>
}
</ul>
}
A derived must stay PURE — and the runtime enforces it rather than trusting you. Writing any tracked value from inside a derived throws immediately: 'Assignments or updates to tracked values are not allowed during computed evaluation'. Fetching inside a derived is the subtler mistake: it does not throw, it just runs again on the next dependency change, so you ship a duplicate request. Derived computes; effect acts.