Skip to content

Build1 publisher3 min readPublished

Svelte's runes move reactivity from a compiler label to a function call

A dev.to post argues that $state, $derived and $effect do the jobs of useState, useMemo and useEffect. The equivalence is a match of job names, and the published case for the trade is one 147-component migration.

The Engineer · Build desk

Photograph accompanying Svelte's runes move reactivity from a compiler label to a function call
Photo: vercel.com

What happened

  • Svelte 5 was announced on September 20, 2023 and stabilised in late 2024 according to a dev.to post, and it introduced three runes: $state, $derived and $effect.
  • Svelte 5's Universal Reactivity lets $state and $derived be used in plain .js and .ts files, not only in .svelte components.
  • A migration credited to Strapi took a 147-component app's bundle from 382 KB to 171 KB, which the post calls a 55% reduction.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision If the prediction that $: and export let get cycled out holds, teams still on that syntax have a rewrite whose date the framework picks for them.
  • capability Reactive state that compiles inside a .ts file means a shared store can be written once and imported by components. The hooks comparison does not describe that part of the change.
  • cost A team budgeting its own migration on the 55% figure is extrapolating from one app. Its build setup and its other changes went unpublished.
  • contradiction The same post credits runes with fixing scaling pain it calls real and says Svelte gave up what made it distinct, and it still lands on staying with Svelte 5.

`$state` holds state, `$derived` computes a value from it, `$effect` runs work when something changes, and those are the jobs the post assigns to `useState`, `useMemo` and `useEffect` [3]. It asserts the equivalence without comparing calling conventions, or how either framework works out what a derivation depends on [21]. The post's own opening pitch for Svelte lists "No dependency arrays" among the things you were spared [5], and it never says runes brought one back.

Svelte 5 changed where you can declare reactivity. Svelte 3 and 4 took it from a label the compiler picked up, `$: doubled = count * 2`; Svelte 5 writes `let doubled = $derived(count * 2)` [4]. The same `$state` and `$derived` also work in plain `.js` and `.ts` files under what Svelte calls Universal Reactivity [9]. The post's account of why the old form ran out is specific: `$:` did not scale to larger apps, and its behaviour outside `.svelte` files was hard to understand and therefore hard to trust [8]. In the Svelte 5 intro video, according to the post, Rich Harris said runes "use function syntax to achieve the same things and more" [6], and he described the change as needed to address scaling issues that were becoming "hairy" [7].

The payoff figure is one migration: Strapi is credited with taking a 147-component app from a 382 KB bundle to 171 KB, which the post calls a 55% reduction [10]. The subtraction is 211 KB, and 211 divided by 382 is 55.2 percent, so the percentage checks out [11]. Those two totals arrive with no build configuration and no list of what else changed during the migration [12]. For the number to transfer, your component tree would have to resemble theirs in bundle composition as well as in component count, and the rewrite would have to have changed nothing else that affects output size.

The cost that lands on a team already shipping Svelte is maintenance. A Medium critique quoted in the post said Svelte is "having an identity crisis" [13], and that "Runes will be the de-facto standard and soon enough $: and export let syntax will be cycled out" [14]. That prediction comes from a critic.

In my view, explicit declaration is the right trade once reactive state has to live outside components, which is the situation `$:` handled badly by the post's own account [8]. The advantage still on offer is the compiler work plus the `.ts` file; the post concedes the first half when it calls Svelte "a prettier structure to convey the identical concept, and a compiler performed additional tasks" [22]. The post's author, who says the framework built to avoid React hooks essentially redeveloped them [19], also wrote: "I'll most likely continue using Svelte 5." [16]

What to watch

  • A Svelte deprecation notice for $: and export let. One would put a date on the critic's prediction, which for now is a forecast.
  • A published migration with a per-chunk bundle breakdown and a list of everything else that changed, so the 55% can be attributed.
  • Whether shared state libraries start shipping $state in .ts files. The useState comparison stops short of that capability.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories