AcademyReact
Error Boundaries
Why a render error unmounts the whole tree by default, how a class component catches it, and what it never catches at all.
3 min readUpdated 3 exercises
The default is total
Throw an error while rendering — in a component body, in render, in a constructor, anywhere below the root — and React does not leave the broken part on screen. It unmounts the entire tree.
That sounds harsh until you compare it with the alternative. A component that half-rendered after failing partway through is not "mostly working" — it is showing a user data it never finished computing, with no way to tell which parts are real. React's authors decided a blank screen is a more honest failure than a plausible-looking wrong one, and Error Boundaries exist to opt back into something less than "blank screen, whole app."
The only class component you still need to write
There is no hook for this. componentDidCatch and getDerivedStateFromError are lifecycle methods on a class component, and nothing in the Hooks API replaces them:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
// Called during render, after a descendant threw. Return the state
// that produces the fallback UI on the next render.
return { hasError: true };
}
componentDidCatch(error, info) {
// Called after render, with the commit already rolled back. This is
// where you report to Sentry — never inside getDerivedStateFromError,
// which must stay a pure function of the error.
logErrorToService(error, info.componentStack);
}
render() {
if (this.state.hasError) return this.props.fallback;
return this.props.children;
}
}The split matters: getDerivedStateFromError decides what to render and must be pure — no logging, no network calls. componentDidCatch is the side-effecting half, called once the fallback has already committed. Mixing the two — logging inside getDerivedStateFromError, or trying to set fallback state from componentDidCatch — works by accident on some React versions and breaks on others.
What it does not catch
This is the part that costs people in interviews, because the name suggests more coverage than it has. An Error Boundary catches errors thrown during React's own rendering work for its descendants, and nothing else:
| Source | Caught? | What actually handles it |
|---|---|---|
| Error thrown in a component's render body | Yes | The boundary |
| Error thrown in a lifecycle method / effect setup | Yes | The boundary |
Error thrown inside an onClick handler | No | try/catch in the handler itself |
A rejected promise from fetch | No | .catch() on the promise |
| An error thrown by the boundary component itself | No | A boundary higher up than this one |
| An error during server-side rendering | No | Framework-level SSR error handling |
Event handlers run outside React's render call stack entirely — by the time onClick fires, rendering already finished, so there is no render pass for the boundary to intercept. Same story for anything async: the throw happens in a microtask or a timer callback, far from any component that is currently rendering. Both need their own explicit handling; a boundary sitting above them does nothing for either.
Where to put them
One Error Boundary at the app root buys you a global fallback — "Something went wrong, reload" — instead of a white screen. That is necessary but not sufficient. If the only boundary in the app sits at the root, one broken widget takes the whole page down with it.
function Dashboard() {
return (
<Layout>
<ErrorBoundary fallback={<ChartError />}>
<RevenueChart />
</ErrorBoundary>
<ErrorBoundary fallback={<ChartError />}>
<UserGrowthChart />
</ErrorBoundary>
</Layout>
);
}Each chart gets its own boundary. A bad API response that crashes RevenueChart now shows one broken tile instead of an empty page — UserGrowthChart keeps rendering because its own render pass was never touched. The rule of thumb: put a boundary around any subtree whose failure should not be correlated with its siblings' success.
Now practice it
2 challenges · 1 quizReading this page is the cheap half. These are the exercises that make you use it.
- Error BoundaryProImplements the getDerivedStateFromError / componentDidCatch split from scratch, including the fallback state transition.ChallengeSolve · MediumChallengeSolve · Medium
- Suspense Resource CacheProPairs naturally with boundaries — a resource cache that throws a promise while loading, caught the same way an error is.ChallengeSolve · HardChallengeSolve · Hard
- Fundamentals · React BasicsCore rendering questions, including what unmounts and what survives when a descendant throws.QuizTake · EasyQuizTake · Easy
Read next
Portals and Rendering Outside the Tree
How createPortal splits DOM position from React-tree position, why modals need it, and where events actually bubble.
3 min
Render Props vs Higher-Order Components
The two pre-hooks patterns for sharing behavior between components, their well-known pitfalls, and why a hook replaces both.
3 min