Components Advanced
ErrorBoundary
StableCatches rendering errors in part of the screen and shows a recoverable fallback.
Wrap independent regions, such as a signature preview, a chart or an integration panel, so a failure in one does not blank the whole page. The default fallback is an accessible alert with a retry button; you can supply your own. ErrorBoundary catches rendering errors only. Handle event and async errors where they happen.
Overview
import * as React from 'react';
import { Button, ErrorBoundary } from '@bindshq/stance-react';
function SignaturePreview({ broken }: { broken: boolean }) {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => setMounted(true), []);
if (broken && mounted)
throw new Error('Template HTML could not be parsed: unexpected </td> at line 14');
return (
<div
style={{ padding: 16, border: '1px solid var(--st-color-border-default)', borderRadius: 12 }}
>
<strong>Priya Nair</strong>
<div>Account Executive · Binds</div>
</div>
);
}
export function Basic() {
const [broken, setBroken] = React.useState(true);
return (
<div style={{ display: 'grid', gap: 12, maxWidth: 460 }}>
<Button size="sm" variant="outline" onClick={() => setBroken((b) => !b)}>
{broken ? 'Fix the template' : 'Break the template'}
</Button>
<ErrorBoundary resetKeys={[broken]}>
<SignaturePreview broken={broken} />
</ErrorBoundary>
</div>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { ErrorBoundary } from '@bindshq/stance-react';Platforms
ErrorBoundary has native counterparts. They share Stance tokens and intent, and use each platform’s own controls and conventions. The live previews on this page are the web implementation.
import { ErrorBoundary } from '@bindshq/stance-react';Examples
import * as React from 'react';
import { Button, ErrorBoundary } from '@bindshq/stance-react';
function SignaturePreview({ broken }: { broken: boolean }) {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => setMounted(true), []);
if (broken && mounted)
throw new Error('Template HTML could not be parsed: unexpected </td> at line 14');
return (
<div
style={{ padding: 16, border: '1px solid var(--st-color-border-default)', borderRadius: 12 }}
>
<strong>Priya Nair</strong>
<div>Account Executive · Binds</div>
</div>
);
}
export function CustomFallback() {
return (
<div style={{ maxWidth: 460 }}>
<ErrorBoundary
fallback={({ reset }) => (
<div
style={{
padding: 16,
border: '1px dashed var(--st-color-border-strong)',
borderRadius: 12,
}}
>
<p style={{ marginTop: 0 }}>The preview isn’t available. Your template is saved.</p>
<Button size="sm" onClick={reset}>
Reload preview
</Button>
</div>
)}
>
<SignaturePreview broken />
</ErrorBoundary>
</div>
);
}Reset on navigation
Changing resetKeys clears the error, e.g. when the user opens another template.
import * as React from 'react';
import { Button, ErrorBoundary } from '@bindshq/stance-react';
function SignaturePreview({ broken }: { broken: boolean }) {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => setMounted(true), []);
if (broken && mounted)
throw new Error('Template HTML could not be parsed: unexpected </td> at line 14');
return (
<div
style={{ padding: 16, border: '1px solid var(--st-color-border-default)', borderRadius: 12 }}
>
<strong>Priya Nair</strong>
<div>Account Executive · Binds</div>
</div>
);
}
export function ResetKeys() {
const [template, setTemplate] = React.useState('broken');
return (
<div style={{ display: 'grid', gap: 12, maxWidth: 460 }}>
<div style={{ display: 'flex', gap: 8 }}>
<Button
size="sm"
variant={template === 'broken' ? 'primary' : 'outline'}
onClick={() => setTemplate('broken')}
>
Legacy template
</Button>
<Button
size="sm"
variant={template === 'ok' ? 'primary' : 'outline'}
onClick={() => setTemplate('ok')}
>
Standard template
</Button>
</div>
<ErrorBoundary resetKeys={[template]} title="This template can’t be previewed">
<SignaturePreview broken={template === 'broken'} />
</ErrorBoundary>
</div>
);
}API reference
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | None | The region to protect. |
fallback | ReactNode | ({ error, reset }) => ReactNode | None | Custom fallback. Defaults to an accessible alert with a retry button. |
onError | (error: Error, info: ErrorInfo) => void | None | Report the error and component stack to your error tracker. |
onReset | () => void | None | Called after the boundary resets. |
resetKeys | readonly unknown[] | None | When any value changes, a caught error is cleared. |
title | string | 'Something went wrong' | Default fallback title. |
description | string | None | Default fallback description. |
retryLabel | string | 'Try again' | Default retry button label. |
Accessibility
- The default fallback uses role="alert", so the failure is announced when it appears.
- Technical details are in a collapsed disclosure, so they do not overwhelm the message.
- The retry button is a native button. After a reset, the region renders again in place.
- Write fallback copy that says what failed and what the person can do next.
Keyboard
| Keys | Action |
|---|---|
| Enter Space | Activates the retry button in the default fallback. |
Usage guidelines
Do
- Wrap independent regions separately so the rest of the screen keeps working.
- Send errors to monitoring with onError.
Don’t
- Wrap the entire app in a single boundary only.
- Show raw stack traces to end users.