Svelte 5 replaces the compiler-magic let reactivity with runes — explicit functions that mark reactive state. It’s a bigger shift than it looks.
$state and $derived
$state declares mutable reactive state. $derived computes a value from it, re-evaluating only when its dependencies change.
<script lang="ts">
let count = $state(0);
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>{count} → {doubled}</button> No more $: label ambiguity. Dependencies are tracked by reading them inside $derived.
$effect is a last resort
Reach for $effect only for genuine side effects — syncing to localStorage, wiring an observer, talking to a non-reactive library. If you’re computing a value, you want $derived instead.
The mental model is simpler once it clicks: state flows in one direction, and you declare what depends on what.