Error boundaries stop one broken component from blanking the page
In a component tree, an uncaught error thrown during render is fatal by default:
React unmounts the entire tree rather than show a half-broken UI, so one
component reading user.name when user is null blanks the whole page. An
error boundary is the seam that stops the blast radius at a subtree. It catches
a render-time crash below it, shows a fallback in place of the broken part, and
leaves the rest of the app — the nav, the sidebar, the other panels — alive and
interactive. Placing boundaries well is the difference between “one widget failed”
and “the app is down.”
A boundary is a component that catches its children’s throws
Error boundaries are a class component (the one place React still needs one), because they use two lifecycle hooks: one to render a fallback, one to report the error. Below it, any render-time throw is caught:
class ErrorBoundary extends React.Component {
state = { failed: false };
static getDerivedStateFromError() {
return { failed: true }; // switch to the fallback UI
}
componentDidCatch(error, info) {
reportToService(error, info); // log it where you can see it
}
render() {
return this.state.failed
? this.props.fallback
: this.props.children;
}
}
Wrap a risky subtree and its crash becomes a contained fallback:
<ErrorBoundary fallback={<p>This panel failed to load.</p>}>
<RevenueChart /> {/* if this throws, only this panel shows the fallback */}
</ErrorBoundary>
Place boundaries at meaningful seams
Where you put boundaries is a design decision about blast radius. One boundary at the very top turns a crash into a whole-app fallback — better than a blank page, but coarse. Boundaries around each independent region — a dashboard’s widgets, a feed’s items, a route — mean a failure in one leaves the others fully usable:
<Dashboard>
<ErrorBoundary fallback={<WidgetError />}><Revenue /></ErrorBoundary>
<ErrorBoundary fallback={<WidgetError />}><Traffic /></ErrorBoundary>
</Dashboard>
{/* Revenue crashing does not touch Traffic */}
Know what a boundary does not catch
The important limitation: boundaries catch errors during rendering, lifecycle,
and constructors of the components below them. They do not catch errors in
event handlers, in async code (setTimeout, promises, fetch callbacks), or in
the boundary’s own render — because those do not happen during React’s render pass.
An error in an onClick you handle with a plain try/catch and, say, a toast; a
failed fetch you handle with the request/success/fail state and an error UI. So the
full resilience story is two-layered: boundaries for the render-path crashes that
would otherwise blank the tree, and ordinary error handling for the async and
event-driven failures that a boundary never sees. The toast-notifications and
retry-with-backoff exercises cover that second layer — the errors boundaries leave
for you — which together with a well-placed boundary is what keeps an app standing
when something inevitably throws.