Skip to content
design
Components

Error boundary

The fence that keeps one broken part of the screen from taking the rest down.

On this page
One broken panel, one message: the boundary draws the fallback where the part was, and the helm display around it keeps working.
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.

The function fallback gets the error and a reset: Try again mounts the children afresh, so a panel can recover without the page.
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

PropTypeDefaultDescription
childrenrequiredReactNode–The part of the page the boundary guards.
fallbackrequiredErrorBoundaryFallback–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.
testIdstring | undefined–Becomes data-testid on the wrapper the fallback draws in; the healthy boundary draws none.
  • 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.