Svelte 12: $derived.by — Multi-Statement Derivations
easy⏱ 5 mincoursesvelte
$derived vs $derived.by
$derived accepts exactly one expression — no semicolons, no for loops, no local let inside it. The moment your computation needs more than that (looping over a cart to sum totals, branching logic, building up a string piece by piece), reach for $derived.by(() => {...}). It's the same dependency-tracking engine underneath; you're just given a real function body to work with.
<script>
let count = $state(4);
// OK: single expression
let doubled = $derived(count * 2);
// NOT valid: $derived only takes an expression
// let sum = $derived(() => { let s = 0; for (...) s += x; return s; });
// Correct: use $derived.by for a function body
let sum = $derived.by(() => {
let s = 0;
for (let i = 0; i < count; i++) s += i;
return s;
});
</script>
Every value you read inside becomes a dependency
In the shop below, total reads items (loops over it), and discount reads total. Because $derived.by tracks reads dynamically — not via a declared dependency array — discount automatically recomputes whenever total changes, which itself recomputes whenever any item's qty changes. Try clicking '+1' on an item and watch discount and total cascade update.
$derived.by is still read-only, still cached
Just like $derived, the value returned by $derived.by is memoized — Svelte only re-runs the function when a tracked dependency actually changes, not on every render, and you can't assign to it directly (total = 5 would be an error). If you need imperative, effectful logic — not a pure computed value — that's what $effect is for, covered next.