Error boundary
The FlareErrorBoundary component wraps Svelte's native <svelte:boundary> to catch errors during rendering and report them to Flare with Svelte-specific context including the component name, component hierarchy, and error origin.
Basic usage
Wrap your component tree (or parts of it) in the FlareErrorBoundary:
<script>
import { FlareErrorBoundary } from '@flareapp/svelte';
</script>
<FlareErrorBoundary>
<MyComponent />
</FlareErrorBoundary>
Without a failed snippet, the boundary renders nothing when an error is caught.
Displaying a fallback UI
Use the failed snippet to render a fallback when an error is caught. The snippet receives the caught error and a reset function to clear the error state:
<script>
import { FlareErrorBoundary } from '@flareapp/svelte';
</script>
<FlareErrorBoundary>
<App />
{#snippet failed(error, reset)}
<div>
<h2>Something went wrong</h2>
<p>{error.message}</p>
<button onclick={reset}>Try again</button>
</div>
{/snippet}
</FlareErrorBoundary>
Resetting the error boundary
You can automatically reset the error boundary when certain values change using the resetKeys prop. When any value in the array changes between renders (compared per-index via Object.is), the boundary automatically resets and re-renders its children.
A common use case is resetting the boundary when the user navigates to a different page:
<script>
import { page } from '$app/state';
import { FlareErrorBoundary } from '@flareapp/svelte';
let { children } = $props();
</script>
<FlareErrorBoundary
resetKeys={[page.url.pathname]}
onReset={(error) => {
console.log('Navigated away from error, previous error:', error?.message);
}}
>
{@render children()}
{#snippet failed(error, reset)}
<p>Something went wrong.</p>
{/snippet}
</FlareErrorBoundary>
The onReset callback fires when the error boundary resets (either via reset() from the failed snippet or via resetKeys changing). It receives the previous error, allowing conditional cleanup.
Lifecycle hooks
The error boundary provides three hooks that fire at different stages of the error reporting lifecycle. The beforeSubmit and afterSubmit callbacks receive a context object with the following shape:
{
svelte: {
componentName: string | null;
componentHierarchy: string[];
errorOrigin: 'render' | 'event' | 'effect' | 'unknown';
}
}
Both types are exported from @flareapp/svelte as FlareSvelteContext and SvelteErrorOrigin.
The callbacks are not wrapped in a try/catch. If a hook throws, the boundary itself will crash, so keep the logic defensive.
beforeEvaluate
Fires before the component hierarchy context is built. This is the place to attach custom context, tags, or user information to the Flare report:
<script>
import { flare } from '@flareapp/js';
import { FlareErrorBoundary } from '@flareapp/svelte';
</script>
<FlareErrorBoundary
beforeEvaluate={({ error }) => {
flare.addContext('user', { id: currentUser.id });
flare.addContext('feature-flags', getActiveFlags());
}}
>
<App />
</FlareErrorBoundary>
beforeSubmit
Fires after the component hierarchy context is built but before the error is reported to Flare. The callback receives the context and must return a (possibly modified) context object. Use this to filter or enrich the report context:
<FlareErrorBoundary
beforeSubmit={({ error, context }) => {
return {
...context,
svelte: {
...context.svelte,
componentHierarchy: context.svelte.componentHierarchy.filter(
(name) => name !== 'ThirdPartyWrapper',
),
},
};
}}
>
<App />
</FlareErrorBoundary>
afterSubmit
Fires after flare.report() is called. Note that the report is sent asynchronously, so this callback runs after the report is initiated, not after the network request completes. Use this for side effects like logging to a secondary service, showing a toast, or updating app state:
<FlareErrorBoundary
afterSubmit={({ error, context }) => {
console.error('Caught by FlareErrorBoundary:', error);
console.error('Reported context:', context);
}}
>
<App />
</FlareErrorBoundary>
Filtering errors
The error boundary hooks above are designed for adding context and running side effects; they cannot prevent an error from being reported. To filter or suppress errors, use the core JavaScript client hooks via flare.configure().
Note that the boundary's beforeEvaluate and beforeSubmit are different hooks from flare.configure({ beforeEvaluate, beforeSubmit }) — they share the same name but have different signatures and capabilities. The execution order when both are configured is:
- Boundary
beforeEvaluate(adds context, cannot suppress) flare.report()is called internally, which triggers:- Client
beforeEvaluateviaflare.configure()(can suppress by returningfalse) - Client
beforeSubmitviaflare.configure()(can suppress or modify the report) - Report is sent to Flare
- Boundary
afterSubmit
To suppress specific errors, use the client-level hooks:
flare.configure({
beforeEvaluate: (error) => {
if (error.message.includes('Boring error')) {
return false; // Don't report this error
}
return error;
},
});
See the client hooks documentation for more details.
Using your own error boundaries
If you have a component that already uses <svelte:boundary> to catch errors, the error won't bubble up to FlareErrorBoundary and won't be reported automatically. You can use createFlareErrorHandler() to report errors from your own boundary:
<script>
import { createFlareErrorHandler } from '@flareapp/svelte';
const handleError = createFlareErrorHandler();
</script>
<svelte:boundary onerror={handleError}>
<slot />
{#snippet failed(error, reset)}
<p>Something went wrong. <button onclick={reset}>Retry</button></p>
{/snippet}
</svelte:boundary>
You can also combine Flare's error boundary at the root of your app with your own boundaries further down the tree for specific sections.
Component hierarchy
When the error boundary catches an error, it sends Svelte-specific context including the component name, the full component hierarchy, and the error origin:
{
"context": {
"svelte": {
"componentName": "BuggyComponent",
"componentHierarchy": [
"BuggyComponent",
"NestedParent",
"DashboardPage",
"+layout"
],
"errorOrigin": "render"
}
}
}
The full component hierarchy is available when the Flare preprocessor is enabled. Without the preprocessor, the hierarchy falls back to stack frame extraction and will typically contain only the component that threw.
Enabling the component hierarchy
Svelte does not expose a public API to walk the component tree. To work around this, @flareapp/svelte ships a Svelte preprocessor that injects a lightweight registration call into every component. This builds a parent-child chain using Svelte's context API (setContext / getContext), which the error boundary reads when an error is caught.
The easiest way to add it is by wrapping your config with withFlareConfig in svelte.config.js:
import { withFlareConfig } from '@flareapp/svelte/config';
export default withFlareConfig({
// your existing config (kit, compilerOptions, etc.)
});
withFlareConfig automatically injects the preprocessor alongside any existing preprocessors. It won't inject it twice if it's already present.
You can also use flarePreprocessor() directly for manual control:
import { flarePreprocessor } from '@flareapp/svelte/config';
export default {
preprocess: [flarePreprocessor()],
};
If you already have other preprocessors (e.g. vitePreprocess), add flarePreprocessor() to the array alongside them.
How it works
The preprocessor adds two lines to the top of every component's <script> block at build time:
import { __flareRegisterComponent } from '@flareapp/svelte';
__flareRegisterComponent('ComponentName', 'src/lib/ComponentName.svelte');
At runtime, each component:
- Reads the parent's registration via
getContext(), - Creates a node
{ name, file, parent }linking to that parent, - Registers itself via
setContext()so its children can find it.
When the error boundary catches an error, it looks up the erroring component (identified from the error's stack trace) in the registry and walks the .parent chain to build the full hierarchy.
Trade-offs
The preprocessor is optional — everything works without it, but the component hierarchy will be limited to what the stack trace reveals (usually one component).
| With preprocessor | Without preprocessor | |
|---|---|---|
| Component hierarchy | Full parent chain from error to root | Only components visible in the stack trace (usually 1) |
| Runtime cost | One setContext + one getContext per component mount |
None |
| Bundle size | ~2 lines injected per component | No change |
| Build step | Wrap config with withFlareConfig or add preprocessor to svelte.config.js |
No build changes |
Excluding components
If you want to skip specific files (e.g. a design system library), pass an exclude regex:
// With withFlareConfig:
withFlareConfig({ /* ... */ }, {
exclude: /node_modules|\.stories\.svelte/,
})
// Or with flarePreprocessor directly:
flarePreprocessor({
exclude: /node_modules|\.stories\.svelte/,
})
Without the preprocessor
Without the preprocessor, component names are extracted from the error's stack frames by looking for .svelte file references. In development builds, functionName is preferred (more reliable). In production builds, the file name is used as a fallback. componentName is the first component found, or null if no .svelte file could be identified.
Error origin
The error origin is determined by analyzing stack frame patterns. This heuristic classification helps with filtering and grouping errors:
| Error Origin | Description |
|---|---|
event |
Error originated from an event handler (e.g. onclick, onchange, addEventListener) |
effect |
Error originated from an asynchronous effect (e.g. queueMicrotask, Promise.then, MutationObserver) |
render |
Error occurred during rendering (detected by .svelte files in the stack with no event/effect patterns) |
unknown |
No Svelte-related frames found in the stack |
The priority order is: event > effect > render > unknown.
Props reference
| Prop | Type | Description |
|---|---|---|
children |
Snippet |
The child content to render inside the boundary. |
failed |
Snippet<[error: Error, reset: () => void]> |
Snippet to display when an error is caught. Receives the error and a reset function. |
resetKeys |
unknown[] |
Values that trigger an automatic reset when changed (compared per-index via Object.is). |
beforeEvaluate |
({ error }) => void |
Called before the component context is built. |
beforeSubmit |
({ error, context }) => FlareSvelteContext |
Called before submitting. Return a (possibly modified) context. |
afterSubmit |
({ error, context }) => void |
Called after flare.report() is called (the report is sent asynchronously). |
onReset |
(error: Error | null) => void |
Called when the error boundary is reset; receives the previous error (or null). |