Skip to content

ErrorBoundary

Terminal window
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/error-boundary.json
See it liveBreak it, then recover - by retrying inside, or by changing the key from outside.→

What it catches is narrower than products expect. It catches errors thrown while rendering, below itself. It does not catch:

  • a failed fetch — that is a value a component receives and shows, which is QueryState;
  • an error in an event handler — nothing catches those but the handler;
  • anything thrown after the render, in a timer or a promise.

So this is the last line: something that should never have thrown did, and the alternative is a white page with the product’s name at the top.

<ErrorBoundary
resetKey={route}
onError={report}
labels={{ title: 'Something went wrong', body: '…', details: 'Details', retry: 'Try again' }}
>
<Screen />
</ErrorBoundary>

A class component, and the only one in the set. Not a style choice: React gives no hook for catching a render error, and componentDidCatch exists nowhere else.

resetKey is what un-crashes it. Without one, a boundary that has caught once stays caught — the reader navigates away from the broken screen and the crash follows them, because nothing told the boundary the reason had gone. Pass the route.

It clears on a changed key rather than on any re-render: resetting whenever the parent renders re-runs the throwing child at once, and the screen flickers between the crash and the fallback for as long as the cause is there.

The message is kept, and folded away. The reader cannot fix it, but the reader is who files it — and a screen that hides the one string worth quoting turns a bug report into a guess. It sits inside a closed <details>, because it is evidence rather than an instruction.

labels is required. They are the only text here, and English inside a primitive is text no product can translate.

Props

Read from the types in ui/error-boundary.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.

PropTypeDefault
childrenrequiredReactNode
fallback(error: Error, reset: () => void) => ReactNodeDraw something else instead of the default screen.
resetKeyunknownChange this to clear the error - a route, usually.
onError(error: Error, info: ErrorInfo) => voidSomewhere to send it. The product owns whether that is a log, a file or a service - the boundary only knows it happened.
labelsrequiredErrorBoundaryLabelsThe words on the default screen. Required, and not defaulted: they are the only text here, and English inside a primitive is text no product can translate.

Accessibility

The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 6 rules applied and passed, and none failed. The accessibility report has every item.

History

Each release that changed ui/error-boundary.tsx - the file a product copies - newest first.

  1. v0.23.0
    • feat the screens between asking for data and showing it bc3f636