Derived State

Chapter 3
Derived State and Effects
$derived for values computed from other state, and $effect for side effects

Two more runes complete Svelte's reactivity core. $derived computes a value from other reactive state and keeps it up to date automatically — the counterpart to Vue's computed, Angular's computed signal, and React's useMemo. $effect runs a side effect whenever the state it reads changes — the counterpart to watch/useEffect. Both, like $state, track their dependencies automatically.

$derived — A Computed Value

<script> let price = $state(100); let quantity = $state(2); let total = $derived(price * quantity); </script> <p>Total: {total}</p>

$derived(price * quantity) creates a value that automatically recalculates whenever price or quantity changes. Notice the syntax is even lighter than the others: you pass the expression itself, not a function returning it (no () =>). It's read as a plain variable ({total}), just like $state. Like Vue's computed and Angular's computed, it's cached (only recomputes when a dependency changes) and its dependencies are auto-tracked — no dependency array like React's useMemo.

$derived.by — For Multi-Line Logic

let todos = $state([/* ... */]); let showDone = $state(false); let visible = $derived.by(() => { if (showDone) return todos; return todos.filter((t) => !t.done); });

When the derivation needs more than a single expression, $derived.by(() => {...}) takes a function with a return — useful for filtering, conditionals, or any multi-step computation. This is the natural home for "filter a list before rendering it," the same pattern seen in Vue's computed filter and React's filter-before-map.

$effect — Running a Side Effect

<script> let count = $state(0); $effect(() => { console.log(`count is now ${count}`); }); </script>

$effect(() => {...}) runs its function after the component mounts, then again whenever any reactive value it reads changes — automatically tracked, no dependency list. This is for genuine side effects: logging, syncing to localStorage, manually touching the DOM or a third-party library. It's the equivalent of React's useEffect with auto-detected dependencies, or Vue's watchEffect.

Effect Cleanup

$effect(() => { const id = setInterval(() => console.log('tick'), 1000); return () => clearInterval(id); // cleanup: runs before re-run, and on unmount });

Returning a function from an $effect gives a cleanup function — run before the effect re-runs, and when the component is destroyed. This is exactly React's useEffect cleanup return, used the same way: anything that "starts" something ongoing (a timer, a listener, a subscription) returns its matching "stop." This is also how you handle teardown that the other frameworks put in a separate lifecycle hook (Vue's onUnmounted, Angular's ngOnDestroy).

Don't reach for $effect to compute a value — use $derived
The most common misuse is using an $effect to set one piece of state from another ($effect(() => { doubled = count * 2 })). That's almost always a $derived in disguise, and the $derived version is simpler, cached, and avoids extra re-render cycles. The same rule as Vue's watch-vs-computed: deriving a value → $derived; doing something with a side effect → $effect. Svelte will even warn you about some of these cases.
This replaced Svelte 4's $: reactive statements
Pre-Svelte-5 code used a label syntax for both jobs: $: total = price * quantity for derivations and $: console.log(count) for effects — the same $: prefix doing double duty, which was a frequent source of confusion. Svelte 5 split them into two clearly-named runes ($derived and $effect). If you see $: in a tutorial, that's the old combined form.
Svelte 5VueReactAngular
$derived(expr)computed(() => ...)useMemo (dep array)computed() signal
$derived.by(() => {...})computed(() => {...})useMemocomputed()
$effect(() => {...})watchEffectuseEffect (auto)effect() signal
return cleanup from $effectonUnmounted / watch stopcleanup returnngOnDestroy

Coding Challenges

Challenge 1

Build a component with firstName and lastName $state values and a fullName $derived combining them, with inputs to edit each — confirming fullName updates automatically.

📄 View solution
Challenge 2

Build a todo list with a "show completed" checkbox, using $derived.by to compute the visible todos based on the checkbox, then render that filtered list.

📄 View solution
Challenge 3

Build a component with a count $state and an $effect that persists count to localStorage on every change, reading the saved value back as the initial value so it survives a refresh — plus a separate $effect with a setInterval and a cleanup return.

📄 View solution

Chapter 3 Quick Reference

  • $derived(expr) — a cached, auto-tracked computed value; pass the expression, not a function
  • $derived.by(() => {...}) — the function form for multi-line / conditional logic
  • $effect(() => {...}) — runs a side effect on mount and whenever read state changes (auto-tracked)
  • Return a function from $effect for cleanup (= React's useEffect cleanup)
  • Rule of thumb: deriving a value → $derived; a side effect → $effect
  • These split Svelte 4's combined $: reactive statements into two clear runes
  • Next chapter: bindings and events — bind:value, event handlers, class:/style: directives