Svelte 5 Runes: A React Developer's Guide to Reactivity
Continuing my series on Svelte topics, today we’re talking about runes. If you’re coming from React, this is is going to be a different way to work with reactivity in modern JavaScript.
These blog posts might be what is considered the basics, but it helps me learn and think through the topics if I work on blog posts around the important things that every developer needs to know.
What Are Runes?
In Svelte 5, runes are special symbols that start with the dollar sign ($). They look like regular JavaScript functions, but they’re actually compiler directives, reserved keywords that tell the Svelte compiler how to wire up reactivity during the build step.
If you’ve used decorators in Python or macros in other languages, runes fill a similar role. They look like standard JavaScript, but the compiler transforms them into something more powerful behind the scenes.
Let’s walk through the four runes you’ll use most.
$state — The Engine of Reactivity
$state is the foundation. It declares reactive state in your component.
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>{count}</button>
In React, useState returns an immutable value and a setter function, so you always need that setCount call. In Svelte, $state returns a deeply reactive proxy. You just mutate the value directly, and the compiler handles the rest. No setter function, no spread operators for nested objects. It just works.
$derived — Computed Values Without Dependency Arrays
In React, you’d reach for useMemo here, and you’d need to explicitly declare a dependency array so React knows when to recalculate.
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>
Dependency arrays are prone to human error. We forget what depends on what, and that leads to stale data or unnecessary recalculations. $derived automatically tracks whatever state variables are used inside of it. No dependency array needed. It just reads what it reads, and recalculates when those values change.
$effect — Side Effects That Actually Make Sense
This is the equivalent of useEffect in React, which is notoriously tricky. Missing dependencies, stale closures, infinite loops… all the big gotchas are in useEffect calls.
In Svelte, $effect is used to synchronize state with external systems, like writing to local storage or updating a canvas:
<script>
let theme = $state('dark');
$effect(() => {
localStorage.setItem('theme', theme);
});
</script>
Just like $derived, it automatically tracks its dependencies and only runs when the state it reads actually changes. No dependency array, no cleanup function gotchas. It runs when it needs to run. That’s it.
$props — Clean Component Interfaces
Every framework needs a way to pass data into components. In Svelte 5, $props makes this look like standard JavaScript object destructuring:
<script>
let { name, age, role = 'viewer' } = $props();
</script>
<p>{name} ({age}) - {role}</p>
Default values, rest parameters, renaming … it all works exactly how you’d expect from CommonJS. If you know destructuring, you already know $props. It’s readable, predictable, and there’s nothing new to learn.
Runes Me Over
You’ve probably noticed a theme. Svelte 5 runes eliminate a whole class of bugs that come from manually managing dependencies. React makes you think about when things should update. Svelte’s goal is to figure it out for you at compile time.
/ Web-development / javascript / svelte / React