Error boundary
The fence that keeps one broken part of the screen from taking the rest down.
On this page
Code
import { ErrorBoundary } from "@bosunkit/components/error-boundary";
import type { ReactElement } from "react";
/** A chart that cannot draw its data: it throws while it renders. */
function BrokenChart(): ReactElement {
throw new Error("no data");
}
/** The chart throws, so only the browser may render the example: a boundary cannot catch a prerender error. */
export const readsClock: boolean = true;
export const description: string =
"A reading panel whose chart threw: the boundary draws the message in its place, and the rest of the screen is untouched.";
export default function Fallback(): ReactElement {
return (
<div style={{ border: "1px solid var(--border)", borderRadius: "var(--radius-md)", padding: "var(--space-md)" }}>
<ErrorBoundary fallback={<p>This reading cannot be shown.</p>}>
<BrokenChart />
</ErrorBoundary>
</div>
);
}
ErrorBoundary catches an error thrown while its children render and draws its fallback in their place, so a
chart that cannot draw leaves the screen around it untouched. The fallback is the caller's words: a reading that
cannot be shown is not an alert, and the message says what is missing in the crew's language.
What it catches, and what it cannot. A boundary handles errors React routes from the descendant tree, including rendering and React-managed lifecycle work. It does not catch ordinary event-handler errors, detached asynchronous callbacks, server-rendering errors or failures in the boundary itself. Handle those at their operation boundary. See the React error-boundary contract. Boundaries sit around a feature subtree — a panel, a page's contents — never around the whole app, so no single break takes everything down.
Recovering. A function fallback receives the error and a reset that mounts the children again. A remount — a
new key — does the same. onError hands the error to the caller to log.
Code
import { ErrorBoundary } from "@bosunkit/components/error-boundary";
import { Button } from "@bosunkit/components/button";
import type { ReactElement } from "react";
import { useState, type Dispatch, type SetStateAction } from "react";
/** A chart that throws while `broken` is set, and draws after. */
function Chart({ broken }: { broken: boolean }): ReactElement {
if (broken) {
throw new Error("no data");
}
return <p>The reading is back.</p>;
}
/** The chart throws, so only the browser may render the example: a boundary cannot catch a prerender error. */
export const readsClock: boolean = true;
export const description: string =
"The function fallback gets the error and a reset: Try again mounts the children afresh, so a panel can recover without the page.";
export default function Recovery(): ReactElement {
const [broken, setBroken]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(true);
return (
<div style={{ border: "1px solid var(--border)", borderRadius: "var(--radius-md)", padding: "var(--space-md)" }}>
<ErrorBoundary
fallback={(_error: unknown, reset: () => void): ReactElement => (
<div style={{ display: "grid", gap: "var(--space-sm)", justifyItems: "start" }}>
<p>This reading cannot be shown.</p>
<Button onClick={(): void => { setBroken(false); reset(); }}>Try again</Button>
</div>
)}
>
<Chart broken={broken} />
</ErrorBoundary>
</div>
);
}
Recover the affected feature
Use a boundary around a chart, figure or feature panel so an error does not remove unrelated navigation and operational context. A reset mounts that subtree again; it does not repair corrupt data, repeat a server write or restore local state discarded by the failed subtree. Keep important drafts above the boundary when they must survive recovery.
Give the fallback a useful explanation and a retry action only when retrying can help. Log diagnostics through
onError without displaying a stack trace or private payload. Handle request failures and event-handler errors
at their operation boundary and render an expected error state, rather than throwing them to obtain styling.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| childrenrequired | ReactNode | – | The part of the page the boundary guards. |
| fallbackrequired | ErrorBoundaryFallback | – | What is drawn in place of the part that threw: an element, or a function of the error and a reset that mounts the children again. Its text is the caller's, translated — a reading that cannot be shown is not an alert, and the words say what is missing. |
| onError | ((error: unknown) => void) | undefined | – | Called with the error when the part below throws, for the caller to log it. |
| testId | string | undefined | – | Becomes data-testid on the wrapper the fallback draws in; the healthy boundary draws none. |
Related
- Section message: explain a failed region with a recovery action.
- Error messages: distinguish failure from an uncertain outcome.
- Report figure: keep an illustration's explanation available when its asset fails.