Skip to content

Reactivity Overview

React’s reactivity is built on immutability — you never mutate state directly; you call a setter and React schedules a re-render. Svelte 5’s reactivity is built on runes and direct assignment — you declare state with $state(), then mutate it as you would any JavaScript variable. Svelte’s compiler detects the assignment and emits the DOM update code. Both models produce the same outcome; the feel is very different.

React
function Counter() {
// Must call useState — raw let gives a stale value
const [count, setCount] = React.useState(0);
return <p>Count: {count}</p>;
}
Svelte
<script>
// $state() marks this as reactive — plain let is not tracked
let count = $state(0);
</script>
<p>Count: {count}</p>
React
function Counter() {
const [count, setCount] = React.useState(0);
// Must call the setter — mutating count directly does nothing
return (
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
);
}
Svelte
<script>
let count = $state(0);
// Direct mutation — no setter needed
</script>
<button onclick={() => count++}>
{count}
</button>

In React you reach for useMemo to avoid recomputing expensive values on every render. In Svelte 5 you use $derived — no dependency array, no hook rules, just an expression.

React
function ShoppingCart({ items }) {
const total = React.useMemo(
() => items.reduce((sum, item) => sum + item.price, 0),
[items] // <-- must list every dependency manually
);
return <p>Total: ${total}</p>;
}
Svelte
<script>
let { items } = $props();
// Dependencies tracked automatically — no array needed
let total = $derived(
items.reduce((sum, item) => sum + item.price, 0)
);
</script>
<p>Total: ${total}</p>

useEffect becomes $effect. The most welcome change: no dependency array. Svelte tracks which $state variables your effect reads and re-runs automatically when they change.

React
function TitleUpdater({ title }) {
React.useEffect(() => {
document.title = title;
// Must list title in the array or the effect goes stale
}, [title]);
return null;
}
Svelte
<script>
let { title } = $props();
// Runs when title changes — Svelte tracks it automatically
$effect(() => {
document.title = title;
// Return a cleanup function just like useEffect
return () => { document.title = 'App'; };
});
</script>

Try it: reactive counter with all three runes

Section titled “Try it: reactive counter with all three runes”
How do you update a $state variable in Svelte 5?
What is the Svelte 5 equivalent of React's useEffect with a dependency array?
Why does Svelte $derived not need a dependency array like React useMemo?