Error boundary
The FlareErrorBoundary component uses Vue's onErrorCaptured hook to catch errors thrown by descendant components during setup, rendering, lifecycle hooks, watchers, and event handlers. When an error is caught, it is automatically reported to Flare with Vue-specific context including a structured component hierarchy.
Basic usage
Wrap your component tree (or parts of it) in the FlareErrorBoundary:
<script setup>
import { FlareErrorBoundary } from '@flareapp/vue';
</script>
<template>
<FlareErrorBoundary>
<MyComponent />
</FlareErrorBoundary>
</template>
Without a fallback slot, the boundary renders nothing when an error is caught.
Displaying a fallback UI
The fallback slot is a scoped slot. Provide plain content for a static fallback:
<FlareErrorBoundary>
<App />
<template #fallback>
<p>Something went wrong.</p>
</template>
</FlareErrorBoundary>
Or use the slot props to render the error and let the user retry:
@verbatim
<FlareErrorBoundary>
<App />
<template #fallback="{ error, componentHierarchy, resetErrorBoundary }">
<div>
<h2>Something went wrong</h2>
<p>{{ error.message }}</p>
<pre>{{ componentHierarchy.join('\n') }}</pre>
<button @click="resetErrorBoundary">Try again</button>
</div>
</template>
</FlareErrorBoundary>
@endverbatim
The fallback slot receives:
| Slot prop | Type | Description |
|---|---|---|
error |
Error |
The error that was caught. |
componentHierarchy |
string[] |
Component names from the erroring component up to the app root. |
componentHierarchyFrames |
ComponentHierarchyFrame[] |
Structured frames with component, file, and optional props. |
componentProps |
Record<string, unknown> | undefined |
The erroring component's props. Only present when attachProps is enabled. |
resetErrorBoundary |
() => void |
Clears the error state and re-renders the default slot. |
Resetting the error boundary
You can automatically reset the boundary when certain values change using the resetKeys prop. When any value in the array changes (compared 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 setup>
import { useRoute } from 'vue-router';
import { FlareErrorBoundary } from '@flareapp/vue';
const route = useRoute();
</script>
<template>
<FlareErrorBoundary
:reset-keys="[route.path]"
:on-reset="(error) => {
console.log('Navigated away from error, previous error:', error?.message);
}"
>
<RouterView />
<template #fallback>
<p>Something went wrong.</p>
</template>
</FlareErrorBoundary>
</template>
The onReset callback fires when the boundary resets (either via resetErrorBoundary() from the fallback or via resetKeys changing). It receives the previous error, which lets you branch on what went wrong:
<script setup>
import { useQueryClient } from '@tanstack/vue-query';
import { FlareErrorBoundary } from '@flareapp/vue';
const queryClient = useQueryClient();
</script>
<template>
<FlareErrorBoundary
:on-reset="(error) => {
console.log('Recovering from:', error?.message);
queryClient.invalidateQueries();
}"
>
<App />
<template #fallback="{ resetErrorBoundary }">
<button @click="resetErrorBoundary">Retry</button>
</template>
</FlareErrorBoundary>
</template>
Lifecycle hooks
The error boundary provides three hooks that fire at different stages of the error reporting lifecycle. All three callbacks receive { error, instance, info }, where instance is the Vue component that threw (or null) and info is Vue's lifecycle hook string (e.g. "setup function", "mounted hook", "component event handler").
The beforeSubmit and afterSubmit callbacks additionally receive a context object with the following shape:
{
vue: {
info: string; // Vue-provided lifecycle hook string
errorOrigin: ErrorOrigin; // Normalized category
componentName: string;
componentProps?: Record<string, unknown>;
componentHierarchy: string[];
componentHierarchyFrames: {
component: string;
file: string | null;
props?: Record<string, unknown>;
}[];
route?: RouteContext; // Present when Vue Router is detected
}
}
These types are exported from @flareapp/vue as FlareVueContext and ComponentHierarchyFrame.
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:
<FlareErrorBoundary
:before-evaluate="({ error, instance, info }) => {
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 must return a (possibly modified) context object. Use this to filter or enrich the report context:
<FlareErrorBoundary
:before-submit="({ error, instance, info, context }) => {
return {
...context,
vue: {
...context.vue,
componentHierarchy: context.vue.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
:after-submit="({ error, instance, info, context }) => {
console.error('Caught by FlareErrorBoundary:', error);
console.error('Lifecycle info:', info);
console.error('Reported context:', context);
}"
>
<App />
</FlareErrorBoundary>
Filtering errors
The 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 onErrorCaptured to render a custom fallback UI, the error won't bubble up to FlareErrorBoundary and won't be reported automatically. You can manually report errors to Flare from your own boundary:
<script setup>
import { onErrorCaptured, ref } from 'vue';
import { flare } from '@flareapp/js';
const error = ref(null);
onErrorCaptured((err) => {
flare.report(err);
error.value = err;
return false;
});
</script>
You can also combine Flare's boundary at the root of your app with your own boundaries further down the tree for specific sections.
Capturing component props
Vue's onErrorCaptured gives the boundary access to the live component instance. You can capture the erroring component's props by enabling attachProps:
<FlareErrorBoundary :attach-props="true">
<App />
</FlareErrorBoundary>
When enabled, props are attached both at context.vue.componentProps (the erroring component's props) and per-frame on context.vue.componentHierarchyFrames (each frame's own props).
Props capture is opt-in because props may contain sensitive data. Values are serialized with the following rules:
- Functions become
"[Function]", symbols become"[Symbol]", bigints are converted to strings, circular references become"[Circular]", class instances,Date,RegExp,Map,Set, etc. become"[Object]". - Symbol-keyed properties are dropped.
- Strings longer than 1000 characters are truncated, with a
"…[truncated N chars]"suffix appended. - Arrays longer than 100 items are trimmed, with a final
"[… N more items]"entry appended. - Objects with more than 100 keys are trimmed, with an extra
"…": "[N more keys]"entry appended.
You can control serialization with two additional props:
| Prop | Default | Description |
|---|---|---|
propsMaxDepth |
2 |
How deep nested objects and arrays are serialized before collapsing to "[Object]" / "[Array]". |
propsDenylist |
DEFAULT_PROPS_DENYLIST |
A RegExp tested against each key at every depth. Matching keys are replaced with "[redacted]". By default, a custom value is merged with the built-in denylist (both patterns apply). Set replaceDefaultDenylist to true to use only your custom pattern. |
The default denylist covers common sensitive terms (password, token, secret, authorization, cookie, api_key, session, csrf, ssn, card_number, cvv, …). Since custom denylists extend the default automatically, you can simply pass additional patterns:
<FlareErrorBoundary :attach-props="true" :props-denylist="/internalId/i">
<App />
</FlareErrorBoundary>
If you need to replace the default entirely (e.g. it matches too aggressively for your use case), pass :replace-default-denylist="true":
<FlareErrorBoundary
:attach-props="true"
:props-denylist="/myCustomPattern/i"
:replace-default-denylist="true"
>
<App />
</FlareErrorBoundary>
Component hierarchy context
When the error boundary catches an error, it walks the $parent chain of the erroring component and sends both a flat string array and structured frames in the report context:
{
"context": {
"vue": {
"info": "setup function",
"errorOrigin": "setup",
"componentName": "BuggyComponent",
"componentHierarchy": [
"BuggyComponent",
"ParentPage",
"AppLayout",
"App"
],
"componentHierarchyFrames": [
{ "component": "BuggyComponent", "file": "src/components/BuggyComponent.vue" },
{ "component": "ParentPage", "file": "src/pages/ParentPage.vue" },
{ "component": "AppLayout", "file": "src/layouts/AppLayout.vue" },
{ "component": "App", "file": "src/App.vue" }
]
}
}
}
The structured frames enable rich dashboard rendering and sourcemap resolution.
A few notes:
- Component names are read from
$options.__name(set by the SFC compiler for<script setup>) or$options.name, falling back to"AnonymousComponent". - The
filefield comes from$options.__fileand is only available in development builds. It isnullin production. - Traversal stops at the app root (where
$parentisnull) and is bounded at 50 entries as a safety net against pathological parent chains.
Error origin
Vue's info string describes where the error was caught. The boundary also includes a normalized errorOrigin category, which is easier to filter and group on:
errorOrigin |
Example info strings |
|---|---|
setup |
setup function, ref function, async component loader |
render |
render function, component update, scheduler flush |
lifecycle |
mounted hook, beforeUnmount hook, updated hook, directive hook, … |
event |
native event handler, component event handler |
watcher |
watcher getter, watcher callback, watcher cleanup function |
unknown |
Anything Vue emits that doesn't map to the above |
Both development strings and production single-letter codes are mapped.
Vue Router context
If Vue Router is installed on the app, the boundary automatically detects it via app.config.globalProperties.$router and includes the current route in the report context. No configuration required.
{
"context": {
"vue": {
"route": {
"name": "user-profile",
"path": "/users/42",
"fullPath": "/users/42?tab=settings",
"params": { "id": "42" },
"query": { "tab": "settings" },
"hash": "",
"matched": ["AppLayout", "UserProfile"]
}
}
}
}
Route params and query are serialized with the same denylist as componentProps, so values keyed with sensitive names (e.g. token) are redacted.
The route's fullPath is also passed through URL redaction (using the same logic as url.full), so sensitive query-string values are redacted before being included in the report.
Using the boundary with flareVue()
FlareErrorBoundary returns false from onErrorCaptured, which stops the error from propagating to app.config.errorHandler. When you use both the boundary and the flareVue plugin, errors caught by the boundary are reported to Flare exactly once — using the boundary's hooks and options. The plugin's hooks only fire for errors that are not caught by a boundary. Errors outside any boundary are still caught by the plugin.
Props reference
| Prop | Type | Description |
|---|---|---|
resetKeys |
unknown[] |
Values that trigger an automatic reset when changed (compared via Object.is). |
onReset |
(error: Error | null) => void |
Called when the boundary is reset; receives the previous error. |
beforeEvaluate |
({ error, instance, info }) => void |
Called before the component hierarchy context is built. |
beforeSubmit |
({ error, instance, info, context }) => FlareVueContext |
Called before submitting; must return a (possibly modified) context. |
afterSubmit |
({ error, instance, info, context }) => void |
Called after flare.report() is called (the report is sent asynchronously). |
attachProps |
boolean (default false) |
Attach the erroring component's props, plus each frame's props, to the report context. |
propsMaxDepth |
number (default 2) |
Depth limit used when serializing props. |
propsDenylist |
RegExp (default DEFAULT_PROPS_DENYLIST) |
Keys matching this expression are redacted at every depth. |
replaceDefaultDenylist |
boolean (default false) |
When true, propsDenylist replaces the built-in denylist instead of extending it. |