--- title: Introducing runes description: "Rethinking 'rethinking reactivity'" author: The Svelte team authorURL: / --- In 2019, Svelte 3 turned JavaScript into a [reactive language](/blog/svelte-3-rethinking-reactivity). Svelte is a web UI framework that uses a compiler to turn declarative component code like this... ```svelte ``` ...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](/blog/virtual-dom-is-pure-overhead), and because we're hijacking syntax like `let` and `=` instead of using cumbersome APIs, you can [write less code](/blog/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_.
Introducing runes
## Before we begin 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? > **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`](https://learn.svelte.dev/tutorial/declaring-props) keyword and the [`$:`](https://learn.svelte.dev/tutorial/reactive-declarations) 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: ```diff ``` At first glance, this might seem like a step back — perhaps even [un-Svelte-like](https://twitter.com/stolinski/status/1438173489479958536). 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](https://learn.svelte.dev/tutorial/writable-stores) so that you can use it in multiple places. ## Beyond components 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](https://learn.svelte.dev/tutorial/custom-stores) in a `.js` or `.ts` file: ```js 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 `$`: ```diff - ``` 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: ```diff -import { writable } from 'svelte/store'; export function createCounter() { - const { subscribe, update } = writable(0); + let count = $state(0); return { - subscribe, - increment: () => update((n) => n + 1) + get count() { return count }, + increment: () => count += 1 }; } ``` ```diff ``` Note that we're using a [get property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) in the returned object, so that `counter.count` always refers to the current value rather than the value at the time the function was called. ## Runtime reactivity Today, Svelte uses _compile-time reactivity_. This means that if you have some code that uses the `$:` label to re-run automatically when dependencies change, those dependencies are determined when Svelte compiles your component: ```svelte ``` This works well... until it doesn't. Suppose we refactored the code above: ```js // @errors: 7006 2304 const multiplyByHeight = (width) => width * height; $: area = multiplyByHeight(width); ``` Because the `$: area = ...` declaration can only 'see' `width`, it won't be recalculated when `height` changes. As a result, code is hard to refactor, and understanding the intricacies of when Svelte chooses to update which values can become rather tricky beyond a certain level of complexity. Svelte 5 introduces the `$derived` and `$effect` runes, which instead determine the dependencies of their expressions when they are evaluated: ```svelte ``` As with `$state`, `$derived` and `$effect` can also be used in your `.js` and `.ts` files. ## Signal boost Like every other framework, we've come to the realisation that [Knockout](https://knockoutjs.com/) was right all along. Svelte 5's reactivity is powered by _signals_, which are essentially [what Knockout was doing in 2010](https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob). More recently, signals have been popularised by [Solid](https://www.solidjs.com/) and adopted by a multitude of other frameworks. We're doing things a bit differently though. In Svelte 5, signals are an under-the-hood implementation detail rather than something you interact with directly. As such, we don't have the same API design constraints, and can maximise both efficiency _and_ ergonomics. For example, we avoid the type narrowing issues that arise when values are accessed by function call, and when compiling in server-side rendering mode we can ditch the signals altogether, since on the server they're nothing but overhead. Signals unlock _fine-grained reactivity_, meaning that (for example) changes to a value inside a large list needn't invalidate all the _other_ members of the list. As such, Svelte 5 is ridonkulously fast. ## Simpler times ahead Runes are an additive feature, but they make a whole bunch of existing concepts obsolete: - the difference between `let` at the top level of a component and everywhere else - `export let` - `$:`, with all its attendant quirks - different behaviour between `