mirror of
https://github.com/bretzel-app/crumbs.git
synced 2026-10-12 12:06:57 +02:00
Install project-level skills via skills.sh CLI: - svelte5-best-practices (runes, snippets, events, migration) - sveltekit-structure (routing, layouts, error boundaries) - sveltekit-data-flow (load functions, form actions, serialization) - typescript-best-practices (type-first development, discriminated unions) - drizzle-migrations (SQLite schema, query patterns) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.4 KiB
3.4 KiB
svelte:boundary Component
Available in Svelte 5.3+
Two Purposes
- Error boundaries - catch rendering errors
- Pending UI - show loading state while
awaitresolves
Basic Error Boundary
<svelte:boundary onerror={(e, reset) => console.error(e)}>
<RiskyComponent />
{#snippet failed(error, reset)}
<p>Error: {error.message}</p>
<button onclick={reset}>Try again</button>
{/snippet}
</svelte:boundary>
Pending UI (Loading States)
⚠️ Known Bug:
<svelte:boundary>+{@const await}causes infinite navigation loops during client-side page transitions when pages share async queries. See sveltejs/svelte#17717. Use{#await}blocks until this is fixed.
With experimental.async: true (Svelte 5.36+), {@const await} is
possible but not safe for pages with shared queries or navigation:
<!-- ⚠️ Causes navigation loops - use {#await} instead -->
<svelte:boundary>
{#snippet pending()}
<LoadingSpinner />
{/snippet}
{@const data = await loadData()}
<DataView {data} />
</svelte:boundary>
Safe alternative:
{#await loadData()}
<LoadingSpinner />
{:then data}
<DataView {data} />
{:catch error}
<p>Error: {error.message}</p>
{/await}
Combined Error + Pending
Use svelte:boundary for error catching only, with {#await} for
async data:
<svelte:boundary onerror={logError}>
{#snippet failed(error, reset)}
<p>Failed to load user</p>
<button onclick={reset}>Retry</button>
{/snippet}
{#await fetchUser()}
<p>Loading user...</p>
{:then user}
<UserProfile {user} />
{:catch error}
<p>Error: {error.message}</p>
{/await}
</svelte:boundary>
What Gets Caught
Caught:
- Errors during rendering
- Errors in
$effect
NOT Caught:
- Event handler errors (
onclick, etc.) - Errors after
setTimeout - Async errors outside boundary's await
vs +error.svelte
| Feature | svelte:boundary | +error.svelte |
|---|---|---|
| Scope | Component subtree | Route segment |
| Reset | Built-in reset function | Navigate away |
| Pending | Yes (pending snippet) | No |
| Use case | Component-level | Page-level |
Error Tracking Integration
<svelte:boundary
onerror={(error, reset) => {
// Send to Sentry, LogRocket, etc.
errorTracker.captureException(error);
}}
>
<App />
{#snippet failed(error, reset)}
<ErrorFallback {error} {reset} />
{/snippet}
</svelte:boundary>
Nested Boundaries
Inner boundary catches first:
<svelte:boundary>
<!-- Outer fallback -->
{#snippet failed(e)}
<p>Outer caught: {e.message}</p>
{/snippet}
<svelte:boundary>
<!-- Inner fallback -->
{#snippet failed(e)}
<p>Inner caught: {e.message}</p>
{/snippet}
<ComponentThatMightFail />
</svelte:boundary>
</svelte:boundary>
Key Points
- Use
svelte:boundaryfor component-level error isolation - Use
+error.sveltefor route-level error pages pendingsnippet shows only on initial load (not on refresh - no flicker)failedsnippet replaces content on errorresetfunction lets users retry- Errors in event handlers are NOT caught
- Requires
experimental.async: truein svelte.config.js for{@const await} - ⚠️ Bug:
{@const await}+ navigation causes infinite loops (#17717) — use{#await}instead