diff --git a/documentation/blog/2023-09-20-runes.md b/documentation/blog/2023-09-20-runes.md new file mode 100644 index 0000000000..3ae3699434 --- /dev/null +++ b/documentation/blog/2023-09-20-runes.md @@ -0,0 +1,224 @@ +--- +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 `