Rethinking ‘rethinking reactivity’
In 2019, Svelte 3 turned JavaScript into a reactive language. Svelte is a web UI framework that uses a compiler to turn declarative component code like this…
<script>
let count = 0;
function increment() {
count += 1;
}
script>
<button on:click={increment}>
clicks: {count}
button>
…into tightly optimized JavaScript that updates the document when state like count
changes. Because the compiler can ‘see’ where count
is referenced, the generated code is highly efficient, and because we’re hijacking syntax like let
and =
instead of using cumbersome APIs, you can write less code.
A common piece of feedback we get is ‘I wish I could write all my JavaScript like this’. When you’re used to things inside components magically updating, going back to boring old procedural code feels like going from colour to black-and-white.
Svelte 5 changes all that with runes, which unlock universal, fine-grained reactivity.
Before we beginpermalink
Even though we’re changing how things work under the hood, Svelte 5 should be a drop-in replacement for almost everyone. The new features are opt-in — your existing components will continue to work.
We don’t yet have a release date for Svelte 5. What we’re showing you here is a work-in-progress that is likely to change!
What are runes?permalink
rune /ro͞on/ noun
A letter or mark used as a mystical or magic symbol.
Runes are symbols that influence the Svelte compiler. Whereas Svelte today uses let
, =
, the export
keyword and the $:
label to mean specific things, runes use function syntax to achieve the same things and more.
For example, to declare a piece of reactive state, we can use the $state
rune:
At first glance, this might seem like a step back — perhaps even un-Svelte-like. Isn’t it better if let count
is reactive by default?
Well, no. The reality is that as applications grow in complexity, figuring out which values are reactive and which aren’t can get tricky. And the heuristic only works for let
declarations at the top level of a component, which can cause confusion. Having code behave one way inside .svelte
files and another inside .js
can make it hard to refactor code, for example if you need to turn something into a store so that you can use it in multiple places.
Beyond componentspermalink
With runes, reactivity extends beyond the boundaries of your .svelte
files. Suppose we wanted to encapsulate our counter logic in a way that could be reused between components. Today, you would use a custom store in a .js
or .ts
file:
ts
import {
writable } from 'svelte/store';export function
createCounter () {const {
subscribe ,update } =writable (0);return {
subscribe ,
increment : () =>update ((n ) =>n + 1)};
}
Because this implements the store contract — the returned value has a subscribe
method — we can reference the store value by prefixing the store name with $
:
clicks: {count}
clicks: {$counter}
This works, but it’s pretty weird! We’ve found that the store API can get rather unwieldy when you start doing more complex things.
With runes, things get much simpler:
import { writable } from 'svelte/store';
export function createCounter() {
const { subscribe, update } = writable(0);
let count = $st