Svelte 5 introduces a new reactivity system built on runes, gradually replacing
the old reactive variables. Runes like $state, $derived and $effect give
you more explicit control over how data changes over time.
$state: the source of truth
The $state rune replaces the reactive let declarations of Svelte 4. It works
for primitives as well as objects and arrays, with deep reactivity by default:
<script>
let count = $state(0);
function increment() {
count += 1;
}
</script>
<button onclick={increment}>
Clicks: {count}
</button>
$derived: computed values
$derived replaces $: statements for derived values. The computation is
memoized and only re-runs when its dependencies change:
<script>
let count = $state(0);
let double = $derived(count * 2);
let isEven = $derived(count % 2 === 0);
</script>
<p>{count} doubled is {double} ({isEven ? 'even' : 'odd'})</p>
$effect: side effects
$effect runs after rendering, every time one of its dependencies changes, and
can return a cleanup function:
<script>
let count = $state(0);
$effect(() => {
document.title = `Counter: ${count}`;
return () => {
console.log('cleanup before the next effect');
};
});
</script>
Quick comparison with Svelte 4
| Svelte 4 | Svelte 5 | Role |
|---|---|---|
let x = 0 |
let x = $state(0) |
Local state |
$: y = x * 2 |
let y = $derived(x * 2) |
Derived value |
$: { ... } |
$effect(() => { ... }) |
Side effect |
export let prop |
let { prop } = $props() |
Component props |
Runes are not regular imports: they are keywords compiled by Svelte, recognizable by their
$prefix.Svelte documentation
This approach brings Svelte closer to other modern frameworks while keeping its minimalist syntax. Developers used to the previous version will need to adjust some habits, but the migration stays largely gradual thanks to the backward compatibility the team provides.
In upcoming articles, we'll explore concrete uses of runes in real projects,
notably for state shared between components, for example through a .svelte.js
file:
// counter.svelte.js
export function createCounter(initial = 0) {
let count = $state(initial);
return {
get value() {
return count;
},
increment() {
count += 1;
},
};
}
To go further, the official documentation remains the best resource: https://svelte.dev/docs/svelte/what-are-runes