Skip to main content

await

As of Svelte 5.36, you can use the await keyword inside your components in three places where it was previously unavailable:

  • at the top level of your component’s <script>
  • inside $derived(...) declarations
  • inside your markup

This feature is currently experimental, and you must opt in by adding the experimental.async option wherever you configure Svelte, usually svelte.config.js:

svelte.config
export default {
	
compilerOptions: {
    experimental: {
        async: boolean;
    };
}
compilerOptions
: {
experimental: {
    async: boolean;
}
experimental
: {
async: booleanasync: true } } };

The experimental flag will be removed in Svelte 6.

Boundaries

Currently, you can only use await inside a <svelte:boundary> with a pending snippet:

<svelte:boundary>
	<MyApp />

	{#snippet pending()}
		<p>loading...</p>
	{/snippet}
</svelte:boundary>

This restriction will be lifted once Svelte supports asynchronous server-side rendering (see caveats).

In the playground, your app is rendered inside a boundary with an empty pending snippet, so that you can use await without having to create one.

Synchronized updates

When an await expression depends on a particular piece of state, changes to that state will not be reflected in the UI until the asynchronous work has completed, so that the UI is not left in an inconsistent state. In other words, in an example like this...

<script>
	let a = $state(1);
	let b = $state(2);

	async function add(a, b) {
		await new Promise((f) => setTimeout(f, 500)); // artificial delay
		return a + b;
	}
</script>

<input type="number" bind:value={a}>
<input type="number" bind:value={b}>

<p>{a} + {b} = {await add(a, b)}</p>

...if you increment a, the contents of the <p> will not immediately update to read this —

<p>2 + 2 = 3</p>

— instead, the text will update to 2 + 2 = 4 when add(a, b) resolves.

Updates can overlap — a fast update will be reflected in the UI while an earlier slow update is still ongoing.

Concurrency

Svelte will do as much asynchronous work as it can in parallel. For example if you have two await expressions in your markup...

<p>{await one()}</p>
<p>{await two()}</p>

...both functions will run at the same time, as they are independent expressions, even though they are visually sequential.

This does not apply to sequential await expressions inside your <script> or inside async functions — these run like any other asynchronous JavaScript. An exception is that independent $derived expressions will update independently, even though they will run sequentially when they are first created:

// these will run sequentially the first time,
// but will update independently
let let a: numbera = 
function $derived<number>(expression: number): number
namespace $derived

Declares derived state, i.e. one that depends on other state variables. The expression inside $derived(...) should be free of side-effects.

Example:

let double = $derived(count * 2);

https://svelte.dev/docs/svelte/$derived

@paramexpression The derived state expression
$derived
(await function one(): Promise<number>one());
let let b: numberb =
function $derived<number>(expression: number): number
namespace $derived

Declares derived state, i.e. one that depends on other state variables. The expression inside $derived(...) should be free of side-effects.

Example:

let double = $derived(count * 2);

https://svelte.dev/docs/svelte/$derived

@paramexpression The derived state expression
$derived
(await function two(): Promise<number>two());

If you write code like this, expect Svelte to give you an await_waterfall warning

Indicating loading states

In addition to the nearest boundary’s pending snippet, you can indicate that asynchronous work is ongoing with $effect.pending().

You can also use settled() to get a promise that resolves when the current update is complete:

import { function tick(): Promise<void>

Returns a promise that resolves once any pending state changes have been applied.

tick
, import settledsettled } from 'svelte';
async function function onclick(): Promise<void>onclick() { let updating: booleanupdating = true; // without this, the change to `updating` will be // grouped with the other changes, meaning it // won't be reflected in the UI await function tick(): Promise<void>

Returns a promise that resolves once any pending state changes have been applied.

tick
();
let color: stringcolor = 'octarine'; let answer: numberanswer = 42; await import settledsettled(); // any updates affected by `color` or `answer` // have now been applied let updating: booleanupdating = false; }

Error handling

Errors in await expressions will bubble to the nearest error boundary.

Caveats

As an experimental feature, the details of how await is handled (and related APIs like $effect.pending()) are subject to breaking changes outside of a semver major release, though we intend to keep such changes to a bare minimum.

Currently, server-side rendering is synchronous. If a <svelte:boundary> with a pending snippet is encountered during SSR, only the pending snippet will be rendered.

Breaking changes

Effects run in a slightly different order when the experimental.async option is true. Specifically, block effects like {#if ...} and {#each ...} now run before an $effect.pre or beforeUpdate in the same component, which means that in very rare situations it is possible to update a block that should no longer exist, but only if you update state inside an effect, which you should avoid.

Edit this page on GitHub llms.txt

previous next