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.
Declaring state
Section titled “Declaring state”function Counter() { // Must call useState — raw let gives a stale value const [count, setCount] = React.useState(0); return <p>Count: {count}</p>;}<script> // $state() marks this as reactive — plain let is not tracked let count = $state(0);</script>
<p>Count: {count}</p>Updating state
Section titled “Updating state”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> );}<script> let count = $state(0); // Direct mutation — no setter needed</script>
<button onclick={() => count++}> {count}</button>Derived / computed values
Section titled “Derived / computed values”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.
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>;}<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>Side effects
Section titled “Side effects”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.
function TitleUpdater({ title }) { React.useEffect(() => { document.title = title; // Must list title in the array or the effect goes stale }, [title]); return null;}<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>