Skip to main content

API

Runes

Svelte 5 introduces runes, a powerful set of primitives for controlling reactivity inside your Svelte components and — for the first time — inside .svelte.js and .svelte.ts modules.

Runes are function-like symbols that provide instructions to the Svelte compiler. You don't need to import them from anywhere — when you use Svelte, they're part of the language.

When you opt in to runes mode, the non-runes features listed in the 'What this replaces' sections are no longer available.

Check out the Introducing runes blog post before diving into the docs!

$state

Reactive state is declared with the $state rune:

<script>
	let count = $state(0);
</script>

<button on:click={() => count++}>
	clicks: {count}
</button>

What this replaces

In non-runes mode, a let declaration is treated as reactive state if it is updated at some point. Unlike $state(...), which works anywhere in your app, let only behaves this way at the top level of a component.

$derived

Derived state is declared with the $derived rune:

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

<button on:click={() => count++}>
	{double}
</button>

<p>{count} doubled is {doubled}</p>

The expression inside $derived(...) should be free of side-effects. Svelte will disallow state changes (e.g. count++) inside derived expressions.

What this replaces

The non-runes equivalent would be $: double = count * 2. There are some important differences to be aware of:

  • With the $derived rune, the value of double is always current (for example if you update count then immediately console.log(double)). With $: declarations, values are not updated until right before Svelte updates the DOM
  • In non-runes mode, Svelte determines the dependencies of double by statically analysing the count * 2 expression. If you refactor it...
    ts
    const doubleCount = () => count * 2;
    $: double = doubleCount();
    ...that dependency information is lost, and double will no longer update when count changes. With runes, dependencies are instead tracked at runtime.
  • In non-runes mode, reactive statements are ordered topologically, meaning that in a case like this...
    ts
    $: triple = double + count;
    $: double = count * 2;
    ...double will be calculated first despite the source order. In runes mode, triple cannot reference double before it has been declared.

$effect

To run code whenever specific values change, or when a component is mounted to the DOM, we can use the $effect rune:

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

	$effect(() => {
		// runs when the component is mounted, and again
		// whenever `count` or `double` change,
		// after the DOM has been updated
		console.log({ count, double });

		return () => {
			// if a callback is provided, it will run
			// a) immediately before the effect re-runs
			// b) when the component is destroyed
			console.log('cleanup');
		};
	});
</script>

<button on:click={() => count++}>
	{double}
</button>

<p>{count} doubled is {doubled}</p>

What this replaces

The $effect rune is roughly equivalent to $: when it's being used for side-effects (as opposed to declarations). There are some important differences:

  • Effects only run in the browser, not during server-side rendering
  • They run after the DOM has been updated, whereas $: statements run immediately before
  • You can return a cleanup function that will be called whenever the effect refires

Additionally, you will most likely find you can use effects in all the places where you previously used onMount and afterUpdate (the latter of which will be deprecated in Svelte 5).

$effect.pre

In rare cases, you may need to run code before the DOM updates. For this we can use the $effect.pre rune:

<script>
	import { tick } from 'svelte';

	let div;
	let messages = [];

	// ...

	$effect.pre(() => {
		if (!div) return; // not yet mounted

		// reference `messages` so that this code re-runs whenever it changes
		messages;

		// autoscroll when new messages are added
		if (
			div.offsetHeight + div.scrollTop >
			div.scrollHeight - 20
		) {
			tick().then(() => {
				div.scrollTo(0, div.scrollHeight);
			});
		}
	});
</script>

<div bind:this={div}>
	{#each message as message}
		<p>{message}</p>
	{/each}
</div>

What this replaces

Previously, you would have used beforeUpdate, which — like afterUpdate — is deprecated in Svelte 5.

$props

To declare component props, use the $props rune:

ts
let { optionalProp = 42, requiredProp } = $props();

You can use familiar destructuring syntax to rename props, in cases where you need to (for example) use a reserved word like catch in <MyComponent catch={22} />:

ts
let { catch: theCatch } = $props();

To get all properties, use rest syntax:

ts
let { a, b, c, ...everythingElse } = $props();

If you're using TypeScript, you can use type arguments:

ts
let { a, b, c, ...everythingElse } = $props<MyProps>();

What this replaces

$props replaces the export let and export { x as y } syntax for declaring props. It also replaces $$props and $$restProps, and the little-known interface $$Props {...} construct.

Note that you can still use export const and export function to expose things to users of your component (if they're using bind:this, for example).

How to opt in

Current Svelte code will continue to work without any adjustments. Components using the Svelte 4 syntax can use components using runes and vice versa.

The easiest way to opt in to runes mode is to just start using them in your code. Alternatively, you can force the compiler into runes or non-runes mode either on a per-component basis...

YourComponent.svelte
<!-- this can be `true` or `false` -->
<svelte:options runes={true} />

...or for your entire app:

svelte.config.js
ts
export default {
compilerOptions: {
runes: true
}
};
previous Introduction