Reactive State with $state
Declaring reactive variables with the $state rune, Svelte 5's replacement for plain reactive `let`.
អាន 2 នាទី
In Svelte 5, any value that should update the DOM when it changes needs to be declared with the $state rune. Runes are special compiler-recognized functions — they start with $, and they only work in specific positions Svelte understands. $state is the one that turns a plain variable into a reactive one.
<script>
let count = $state(0);
</script>
<p>Count: {count}</p>
<button onclick={() => count++}>Increment</button>Without $state, count++ would change the variable but the compiler would have no reason to re-run anything that displays it — {count} in the markup would go stale. $state(0) tells the compiler: "track reads and writes of this variable, and re-run whatever depends on it whenever it's written to."
Why not just let?
It's tempting to ask why Svelte needs a rune at all instead of making every let reactive automatically (which is roughly what Svelte 4 did). The problem is that plain JavaScript variables give the compiler no signal about intent — a let might be a loop counter, a cached value, or genuine UI state, and treating all of them as reactive is both wasteful and occasionally wrong. $state makes the intent explicit and lets the compiler generate precise, targeted update code only where you've actually asked for it.
Objects and arrays are deeply reactive
$state doesn't just track reassignment of the variable itself — when you wrap an object or array, Svelte wraps it in a proxy that tracks mutations too:
<script>
let todos = $state([{ text: 'Learn runes', done: false }]);
</script>
<button onclick={() => (todos[0].done = true)}>
Mark done
</button>
<p>{todos[0].done ? 'Done' : 'Not done'}</p>todos[0].done = true mutates a property deep inside the array, and the UI still updates — you didn't need to replace the whole array with a new one (todos = [...]) the way you would in React. The proxy notices exactly which property changed and updates only what reads that property.
State is local to the component instance
Each time a component is created, its $state variables get their own independent copies:
<script>
let count = $state(0);
</script>If you use this component twice on a page, each instance has its own count, starting at 0, changing independently. State declared with $state inside a component is never shared automatically between instances — sharing state across components is a deliberate step you'll see in a later lesson.