Error Boundaries
Class components that catch render/lifecycle errors in children and show a fallback UI instead of crashing.
The core idea#
An error boundary is a component that catches JavaScript errors thrown during rendering, in lifecycle methods, or in constructors of its child tree, logs them, and renders a fallback UI instead of the crashed subtree. Without one, an uncaught render error unmounts the entire React tree in React 16+. 🎯
There is still no Hook for this — you must use a class component (or the react-error-boundary library which wraps one).
The two methods#
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
// Render phase: derive fallback state from the thrown error
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
// Commit phase: side effects — logging to Sentry etc.
componentDidCatch(error, info) {
logToService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
return <p role="alert">Something went wrong.</p>;
}
return this.props.children;
}
}
| Method | Phase | Purpose | Can set state? |
|---|---|---|---|
static getDerivedStateFromError |
render | Compute fallback UI | returns the new state |
componentDidCatch |
commit | Log / report side effects | this.setState allowed |
What it catches vs does NOT catch ⚠️#
| ✅ Catches | ❌ Does NOT catch |
|---|---|
| Errors in child render | Event handlers (use try/catch) |
| Child lifecycle methods | Async code (setTimeout, fetch, promises) |
| Child constructors | Server-side rendering |
| — | Errors thrown in the boundary itself |
Event handlers run outside render, so React can recover normally — wrap them in a plain try/catch and call setState to show an error yourself.
Using react-error-boundary#
The library gives a Hook-friendly API with reset support, avoiding hand-rolled classes:
import { ErrorBoundary } from "react-error-boundary";
function Fallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>{error.message}</p>
<button onClick={resetErrorBoundary}>Retry</button>
</div>
);
}
<ErrorBoundary
FallbackComponent={Fallback}
onReset={() => refetch()} // clear the failed state
resetKeys={[userId]} // auto-reset when a key changes
>
<Profile userId={userId} />
</ErrorBoundary>
🟢 Place boundaries at meaningful granularity — one per route/section — so a failing widget doesn't blank the whole page. Pair them with <Suspense> to handle both loading and error states.
flowchart TD
A["Child component renders"] --> B{"Throws during render/lifecycle?"}
B -->|"No"| C["Normal output"]
B -->|"Yes"| D["Nearest ErrorBoundary catches"]
D --> E["getDerivedStateFromError sets state"]
D --> F["componentDidCatch logs error"]
E --> G["Render fallback UI"]
Interview Q&A#
Q1. Why must an error boundary be a class component?
Only class lifecycle methods static getDerivedStateFromError and componentDidCatch expose the caught error. React provides no Hook equivalent, so even in modern codebases you write one class (or use react-error-boundary, which wraps one internally).
Q2. What does an error boundary NOT catch?
Errors in event handlers, asynchronous code (timeouts, promises, fetch callbacks), server-side rendering, and errors thrown in the boundary itself. These occur outside the child render/lifecycle path it monitors.
Q3. Difference between getDerivedStateFromError and componentDidCatch?
getDerivedStateFromError runs in the render phase and must be pure — it returns state to trigger the fallback. componentDidCatch runs in the commit phase and is for side effects like logging; it also receives the component stack.
Q4. How do you catch an error inside an onClick handler?
Error boundaries don't cover it. Wrap the handler body in try/catch and store the error in state yourself, or call a library helper like useErrorBoundary() from react-error-boundary to forward it to the nearest boundary.
Q5. How do you recover after a boundary trips?
Reset its state — with react-error-boundary call resetErrorBoundary() or change resetKeys. Hand-rolled boundaries reset by remounting the subtree (e.g. changing a key) or exposing a method that sets hasError back to false.