Under The Hood
frontend svelte Svelte 5 (runes mode)

Svelte 5 runes vs legacy let-based reactivity

Last updated
Prerequisites:
Svelte 4 component basics
Understanding of the Svelte compiler
  • svelte
  • runes
  • reactivity
  • signals
  • svelte5
  • compiler

Read at your depth

The practical view

In Svelte 4, reactivity was implicit and compiler-driven: let count = 0; created a reactive variable, and count += 1 updated the DOM. But that magic only worked inside .svelte component top-level scope. Svelte 5 introduces runes — explicit markers: let count = $state(0); creates reactive state anywhere (even in .js/.ts modules); $derived(expr) replaces the $: label; $effect(() => ...) replaces the reactive statement for side effects; and $props() replaces export let. Code written with runes works in plain modules, so state logic is extractable and testable outside components.

The same idea in other frameworks

react equivalent
const [count, setCount] = useState(0); // setCount triggers re-render

Legacy vs modern

Reactive state in a shared module: Svelte 4 vs runes

Svelte 4 reactivity only worked inside components; runes make state a first-class primitive usable in plain modules, extractable and testable.

before → after
Svelte 4 component-only state
<script>
  let count = 0; // implicit reactivity: compiler-scoped
  function inc() { count += 1; }
</script>
Runes module state
export let count = $state(0);
export function increment() { count += 1; }

Interview gotchas

Context

Interviewers want to see the 'why' behind the API change: the failure modes of static analysis, not just the new syntax.

The mechanical answer

Svelte 4's reactivity was a compile-time heuristic: the compiler tracked assignments inside the component to mark invalidations, which broke when state was mutated indirectly — through destructuring, through nested object/array writes in some helper patterns, or when shared across modules. State also could not be extracted to .js modules, forcing components to stay fat. Runes make reactivity explicit and value-based: the runtime tracks reads/writes of the wrapped value, so indirect mutation and cross-module sharing work correctly. The explicit marker also lets the compiler optimize without guessing what a bare variable means.

Trap

The naive answer is 'runes are faster'. The speed story is nuanced — both versions compile to direct DOM updates. The real motivation is correctness and expressiveness: cross-module state, indirect mutation, and predictable semantics. Candidates who answer purely with 'it's the modern way' miss that Svelte 4's model silently failed in real codebases, which is the actual driving force.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.