sveltejs/svelte
Boundary and async
Active contributors: Rich Harris, Dominic Gannaway, Simon H
Purpose
Svelte 5 has first-class support for async work in templates: {#await ...} blocks (since Svelte 3), top-level await in components, the {@async} block, and <svelte:boundary> for declarative error and pending UI. This page covers how those features fit together.
<svelte:boundary>
Implementation: packages/svelte/src/internal/client/dom/blocks/boundary.js (~13 KB).
A boundary captures errors thrown by descendants and pending Promises from async children, and renders the failed / pending snippets when triggered. The runtime hooks into the reactivity engine:
- An
Effectthat sees an error in its child sub-graph propagates up until it finds an enclosingBoundaryEffect. - An async child (an
awaitblock, an async derived) suspends the boundary until it settles. - The boundary's
pendingsnippet is rendered until the async work completes.
Compiler emit: packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js and the matching server visitor.
{#await} blocks
Existing-syntax await blocks render different children based on a Promise's state (pending, fulfilled, rejected). At runtime:
- Client:
packages/svelte/src/internal/client/dom/blocks/await.js. Tracks thePromiseand switches branches on settle. - Server:
packages/svelte/src/internal/server/blocks/await.js. Streams the resolved branch when the Promise settles.
Async runes and {@async}
Async-aware reactivity is gated on async_mode_flag in packages/svelte/src/internal/flags/async.js. The compiler imports svelte/internal/flags/async whenever it sees:
awaitat module/instance scope in runes mode.awaitinside an expression in markup.- An
{@async}block.
When the flag is on:
Reactions can be markedASYNC(packages/svelte/src/internal/client/constants.js).- The
Batchscheduler (internal/client/reactivity/batch.js) supportssuspend()/resume()to defer flushes until pending Promises settle. recent_async_derivedsininternal/client/reactivity/deriveds.jskeeps a list of deriveds that haven't yet resolved.
CI exercises both modes:
- The
Testsjob runs everything withasync_mode_flagavailable. - The
TestNoAsyncjob runsruntime-runeswithSVELTE_NO_ASYNC=true(.github/workflows/ci.yml), which forcesasync_mode_flagtofalseso the legacy fast paths are exercised.
Error handling
packages/svelte/src/internal/client/error-handling.js provides handle_error(...). When an effect throws synchronously or rejects asynchronously, handle_error walks up the effect tree until it finds a boundary or hits the root. If no boundary is found, the error is rethrown to the host environment.
Server-side async
On the server, render() (packages/svelte/src/internal/server/index.js) collects async children as Promises in the Renderer queue and awaits them in order before flushing the parent. This keeps streaming output deterministic.
Key source files
| File | Purpose |
|---|---|
packages/svelte/src/internal/client/dom/blocks/boundary.js |
<svelte:boundary> runtime. |
packages/svelte/src/internal/client/dom/blocks/await.js |
{#await} block runtime (client). |
packages/svelte/src/internal/client/dom/blocks/async.js |
{@async} block runtime. |
packages/svelte/src/internal/client/error-handling.js |
Boundary error propagation. |
packages/svelte/src/internal/client/reactivity/async.js |
Async reaction support glue. |
packages/svelte/src/internal/server/blocks/await.js |
Server-side async streaming. |
packages/svelte/src/internal/flags/async.js |
async_mode_flag toggle. |
packages/svelte/src/internal/client/constants.js |
ASYNC, STALE_REACTION flags. |
Entry points for modification
To change boundary semantics, edit internal/client/dom/blocks/boundary.js and the matching error propagation in error-handling.js. To change async scheduling, edit internal/client/reactivity/batch.js (look for suspend / resume) and internal/client/reactivity/async.js. Always run both async-on and async-off CI configurations locally:
pnpm test runtime-runes # async on
SVELTE_NO_ASYNC=true pnpm test runtime-runes # async offBuilt by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.