Reactivity & Lifecycle (Runes)
React organises reactivity into discrete hooks: useState, useMemo, useEffect, useRef, and more. Svelte 5 replaces all of them with a single unified primitive called runes — compiler-understood keywords prefixed with $. You do not call these like functions in the traditional sense; the compiler reads them at build time and generates optimised reactive code for you.
This module is the course centrepiece. Every subsequent lesson dives into one rune in depth. Start here to get the complete mental map before you dive in.
The runes ↔ hooks mapping
Section titled “The runes ↔ hooks mapping”| React hook | Svelte 5 rune | What it does |
|---|---|---|
useState(init) | $state(init) | Reactive value — mutate directly, no setter needed |
useMemo(() => expr, deps) | $derived(expr) | Computed value — auto-tracks dependencies, no deps array |
useMemo(() => fn(), deps) | $derived.by(() => fn()) | Computed value with a multi-step function body |
useEffect(fn, deps) | $effect(fn) | Side-effect — auto-tracks its dependencies |
useEffect(fn, []) | onMount(fn) | Run once after the component mounts |
useEffect(() => () => cleanup, []) | onDestroy(fn) | Run once when the component is destroyed |
useRef(init) | plain let variable or bind:this | Mutable ref that does not trigger re-renders |
useContext(Ctx) | getContext(key) | Read context provided by an ancestor |
| Component props | $props() | Declare and destructure incoming props |
| Callback props / lifting state | $bindable() | Two-way bindable prop |
The biggest conceptual shift: React hooks require an explicit dependency array. Svelte runes are tracked by the compiler — you never list dependencies manually.
A first runnable counter
Section titled “A first runnable counter”function Counter() { const [count, setCount] = React.useState(0); const doubled = React.useMemo(() => count * 2, [count]);
React.useEffect(() => { document.title = 'Count: ' + count; }, [count]);
return ( <div> <button onClick={() => setCount(c => c + 1)}>+1</button> <p>Count: {count} — Doubled: {doubled}</p> </div> );}<script> let count = $state(0); let doubled = $derived(count * 2);
$effect(() => { document.title = 'Count: ' + count; });</script>
<button onclick={() => count++}>+1</button><p>Count: {count} — Doubled: {doubled}</p>Three runes, zero dependency arrays. The compiler figures out that doubled depends on count, and that the $effect should re-run whenever count changes.