00 - What are Runes?
📋 Jump to Takeaways🎁 In Svelte 4 you wrote let count = 0 and it was magically reactive, while $: meant two completely different things depending on the line. What if you could tell exactly what every variable does just by looking at it?
Runes Concept
Runes are Svelte 5's built-in functions that start with $. They're how you tell Svelte what's reactive, what's computed, what's a side effect, and so on. The name "rune" is just Svelte's branding. Think of them as special compiler instructions.
They're not regular JavaScript functions. The Svelte compiler transforms them at build time, which is why they only work inside .svelte files (and .svelte.ts files).
All Runes at a Glance
| Rune | Purpose | Lesson |
|---|---|---|
$state() |
Reactive variable (UI updates when it changes) | 01 |
$derived() |
Computed value (recalculates when dependencies change) | 02 |
$effect() |
Side effect (runs when dependencies change) | 03 |
$props() |
Component inputs (data passed from parent) | 04 |
$bindable() |
Two-way binding between parent and child | 05 |
$inspect() |
Debug logging (dev only, removed in production) | 08 |
$host() |
Access host element in web components | 08 |
Why Runes Replaced the Old Syntax
In Svelte 4, reactivity was implicit. The compiler magically made things reactive based on syntax:
<!-- Svelte 4: implicit, hard to tell what's what -->
<script>
let count = 0; // reactive? yes, but only because it's top-level
$: doubled = count * 2; // computed? side effect? $: was used for both
$: console.log(count); // same $: syntax for a completely different purpose
export let title; // a prop? yes, but looks like a regular export
</script>The problems with this:
- Unclear intent:
$:was used for both computed values AND side effects - Magic behavior: a plain
letbeing reactive isn't obvious to newcomers - Hard to refactor: moving code outside
.sveltefiles broke reactivity - Every
export letwas bindable: no way to opt out
Svelte 5 fixes this with runes. Each one has a single, clear purpose:
<!-- Svelte 5: explicit, each rune has one job -->
<script>
let count = $state(0); // reactive, explicitly opted in
let doubled = $derived(count * 2); // computed, clearly a derived value
$effect(() => console.log(count)); // side effect, clearly a side effect
let { title } = $props(); // prop, clearly an input from parent
</script>Svelte 4 to Svelte 5 Cheat Sheet
| Svelte 4 | Svelte 5 | What it does |
|---|---|---|
let count = 0 |
let count = $state(0) |
Reactive variable |
$: doubled = count * 2 |
let doubled = $derived(count * 2) |
Computed value |
$: { console.log(count) } |
$effect(() => { console.log(count) }) |
Side effect |
export let title |
let { title } = $props() |
Component prop |
export let value + bind:value |
let { value = $bindable() } = $props() |
Two-way binding |
<slot /> |
{@render children()} |
Default content |
<slot name="header" /> |
{@render header()} |
Named content |
on:click={handler} |
onclick={handler} |
Event handler |
Key Takeaways
- Runes are Svelte 5's explicit reactivity system
- Each rune has one clear purpose (no more overloaded
$:) - They replaced Svelte 4's implicit magic with explicit opt-in
- They start with
$and are compiler instructions, not regular functions - Runes only work inside
.svelteand.svelte.ts/.svelte.jsfiles - The next lessons cover each rune in detail
🎁 You now know runes exist and what each one is for. But how does $state actually make the UI update the instant a value changes, and what happens when you put an object or an array inside it?