December 2024, Revisited: Svelte 5, Runes, and a Year-End of Migration

Eric Greene June 11, 2026

This post is part of our Three-Year Retrospective series: thirty-six posts, one per month, looking back at what actually mattered in software engineering. This one covers December 2024.

Svelte 5 had actually shipped on October 22, 2024, after the longest gestation in the framework's history. But framework releases and framework adoption run on different calendars, and December — the quiet weeks at year end, when feature pressure eases and teams finally tackle the upgrade backlog — is when most Svelte teams we knew actually did the migration. It was a bigger mental shift than the version number suggested, because Svelte 5 changed the framework's most fundamental idea: how reactivity works.

From compiler magic to runes

Svelte's original pitch was that reactivity should be invisible. let count = 0 was reactive because the compiler made it so; $: doubled = count * 2 recomputed by compiler decree. It was beautiful in small components and increasingly slippery in large ones — the rules about when the magic applied (top level of a component only, assignments but not mutations, .svelte files but not .js) accumulated into folklore.

Svelte 5 replaced the magic with runes — compiler-recognized functions that make reactivity explicit:

  • $state(0) declares reactive state, replacing the implicitly-reactive let
  • $derived(count * 2) replaces $: for computed values
  • $effect(() => { ... }) replaces $: for side effects
  • $props() replaces export let for component inputs

The canonical counter shows the whole model:

<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
</script>

<button onclick={() => count += 1}>
  clicked {count} times — doubled: {doubled}
</button>

Under the hood, runes compiled to signals — the fine-grained reactivity model that Solid had championed and that, by 2024, nearly every framework was converging on. The payoff was twofold: updates became precisely targeted rather than component-grained, and reactivity finally worked anywhere, including plain .svelte.js modules, which made shareable reactive logic a first-class pattern instead of a store-shaped workaround.

The community reaction was genuinely split that fall, and it's worth remembering honestly. Plenty of developers had chosen Svelte because let count = 0 was the whole story, and $state looked like boilerplate creep — "now it's just React/Solid with extra steps." The counterargument, which won most people over once they'd lived with it: the old magic didn't scale, and explicit beats folklore.

The migration experience itself

The migration was gentler than the discourse suggested. Svelte 5 ran most Svelte 4 components unchanged in compatibility mode, so you could upgrade the framework first and migrate component-by-component. The npx sv migrate svelte-5 script mechanically converted the bulk of it — let to $state, export let to $props(), on:click to the new onclick event attributes, slots toward snippets, the new composition primitive.

The places that needed human judgment were predictable: complex $: blocks that mixed derivation and side effects (the migration forced you to decide which each line really was — usually an improvement), component events moving from createEventDispatcher to callback props, and stores, which kept working but stopped being the answer to everything. Teams that migrated incrementally through December generally reported the same arc: two days of grumbling, then no desire to go back.

Signals everywhere

The larger December 2024 story was convergence. Solid had signals, Vue's reactivity was signals in all but name, Angular had shipped signals, Preact too — and now Svelte, the framework famous for not exposing a reactivity API, had joined. A TC39 proposal to standardize signals in JavaScript itself was in the works. For working engineers, this convergence was good news: reactive mental models had become portable across frameworks in a way they simply weren't in 2020.

Looking back from June 2026

Runes won. The controversy faded with remarkable speed once codebases migrated, and the patterns runes enabled — reactive logic in plain modules, precise fine-grained updates — became the way Svelte is taught, including by us. Svelte 5's bet that explicitness would age better than magic looks correct from here, and the signals convergence continued across the ecosystem. If anything, December 2024's migration grumbles now read like every migration grumble in this series: the cost was front-loaded and the benefits compounded.

If your team is on Svelte or heading there, Svelte Essentials teaches the framework runes-first, the way it should be learned now, and Svelte for React Programmers maps hooks-and-VDOM instincts onto signals and runes for teams crossing over.