Skip to main content
Glama

MCP Svelte Docs Server

await-expressions.md7.59 kB
# Svelte Await Expressions Svelte 5.36+ introduces experimental support for using `await` expressions directly in your components, enabling powerful asynchronous patterns with automatic loading states and error handling. ## Overview As of Svelte 5.36, you can use the `await` keyword 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 **experimental** and requires opt-in configuration. ## Configuration Enable await expressions by adding the `experimental.async` option in your `svelte.config.js`: ```javascript export default { compilerOptions: { experimental: { async: true, }, }, }; ``` The experimental flag will be removed in Svelte 6. ## Basic Usage ### In Markup ```svelte <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> ``` ### In $derived Declarations ```svelte <script> async function fetchData() { const response = await fetch('/api/data'); return response.json(); } let data = $derived(await fetchData()); </script> <p>Data: {JSON.stringify(data)}</p> ``` ## Boundaries Requirement Currently, you can only use `await` inside a `<svelte:boundary>` with a `pending` snippet: ```svelte <svelte:boundary> <MyApp /> {#snippet pending()} <p>Loading...</p> {/snippet} </svelte:boundary> ``` This restriction will be lifted once Svelte supports asynchronous server-side rendering. > **Note**: In the Svelte playground, your app is rendered inside a > boundary with an empty pending snippet automatically. ## Synchronized Updates When an `await` expression depends on state, changes to that state are synchronized - the UI won't update until the asynchronous work completes, preventing inconsistent states: ```svelte <script> let count = $state(1); async function slowAdd(n) { await new Promise(f => setTimeout(f, 1000)); return n + 1; } </script> <!-- If count changes, display won't show intermediate values --> <p>Count: {await slowAdd(count)}</p> ``` Updates can overlap - a fast update will be reflected while an earlier slow update is still ongoing. ## Concurrency Svelte runs independent `await` expressions in parallel: ```svelte <!-- Both functions run simultaneously --> <p>{await fetchUserData()}</p> <p>{await fetchSettings()}</p> ``` This parallel execution applies to: - Independent expressions in markup - Independent `$derived` expressions (after initial sequential creation) Sequential `await` expressions in `<script>` or inside async functions run normally like standard JavaScript. ## Loading States ### Using $effect.pending() Indicate ongoing asynchronous work with `$effect.pending()`: ```svelte <script> import { tick, settled } from 'svelte'; let isLoading = $derived($effect.pending()); async function handleClick() { let updating = true; // Ensure 'updating' change is reflected immediately await tick(); let color = 'blue'; let answer = 42; // Wait for all updates to complete await settled(); updating = false; } </script> {#if isLoading} <p>Loading...</p> {/if} ``` ### Using settled() The `settled()` function returns a promise that resolves when all state changes and resulting asynchronous work complete: ```svelte <script> import { settled } from 'svelte'; async function complexUpdate() { // Make state changes someState = newValue; // Wait for all related async work to complete await settled(); // Now safe to proceed console.log('All updates complete'); } </script> ``` ## Error Handling Errors in `await` expressions bubble to the nearest error boundary: ```svelte <svelte:boundary> <div> {await mayThrowError()} </div> {#snippet error(err)} <p>Error: {err.message}</p> {/snippet} {#snippet pending()} <p>Loading...</p> {/snippet} </svelte:boundary> ``` ## Common Patterns ### Data Fetching ```svelte <script> async function fetchUser(id) { const response = await fetch(`/api/users/${id}`); if (!response.ok) throw new Error('Failed to fetch user'); return response.json(); } let userId = $state(1); let user = $derived(await fetchUser(userId)); </script> <h1>Welcome, {user.name}!</h1> ``` ### Dependent Async Operations ```svelte <script> let step1 = $derived(await fetchStep1()); let step2 = $derived(await fetchStep2(step1.id)); let result = $derived(await processResult(step2.data)); </script> <p>Final result: {result}</p> ``` ### Form Submission with Loading ```svelte <script> let submitting = $state(false); async function handleSubmit(event) { submitting = true; await tick(); // Ensure UI shows loading state try { await submitForm(new FormData(event.target)); // Handle success } catch (error) { // Handle error } finally { submitting = false; } } </script> <form on:submit|preventDefault={handleSubmit}> <input name="email" type="email" required> <button type="submit" disabled={submitting}> {submitting ? 'Submitting...' : 'Submit'} </button> </form> ``` ## Performance Considerations ### Avoid Waterfalls Be careful of sequential `await` expressions that could run in parallel: ```svelte <!-- ❌ Waterfall - runs sequentially --> <script> let data1 = $derived(await fetch('/api/data1').then(r => r.json())); let data2 = $derived(await fetch('/api/data2').then(r => r.json())); </script> <!-- ✅ Better - runs in parallel --> <script> async function fetchBoth() { const [data1, data2] = await Promise.all([ fetch('/api/data1').then(r => r.json()), fetch('/api/data2').then(r => r.json()) ]); return { data1, data2 }; } let combined = $derived(await fetchBoth()); </script> ``` Svelte will show an `await_waterfall` warning for sequential dependent awaits. ## Caveats and Limitations 1. **Experimental Feature**: APIs may change in breaking ways outside semver major releases 2. **SSR Limitation**: Server-side rendering is synchronous - only `pending` snippets render during SSR 3. **Boundary Requirement**: Currently requires `<svelte:boundary>` with `pending` snippet 4. **Effect Order**: Block effects run before `$effect.pre` when `experimental.async` is enabled ## Migration from {#await} The traditional `{#await}` block syntax still works, but await expressions provide more flexibility: ```svelte <!-- Traditional approach --> {#await promise} <p>Loading...</p> {:then value} <p>Value: {value}</p> {:catch error} <p>Error: {error.message}</p> {/await} <!-- New approach with boundaries --> <svelte:boundary> <p>Value: {await promise}</p> {#snippet pending()} <p>Loading...</p> {/snippet} {#snippet error(err)} <p>Error: {err.message}</p> {/snippet} </svelte:boundary> ``` ## Best Practices 1. **Use boundaries**: Always wrap await expressions in `<svelte:boundary>` 2. **Handle errors**: Provide error snippets for better user experience 3. **Show loading states**: Use `pending` snippets and `$effect.pending()` 4. **Avoid waterfalls**: Use `Promise.all()` for independent async operations 5. **Use `settled()`**: Wait for complex updates to complete before proceeding 6. **Test thoroughly**: Experimental feature - test edge cases carefully

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/spences10/mcp-svelte-docs'

If you have feedback or need assistance with the MCP directory API, please join our Discord server