How to Create an Error Boundary in React
Advertisement
🧩 Scenario
Architecture Walkthrough
Two Lifecycle Methods, Two Responsibilities
An error boundary implements two class lifecycle methods. getDerivedStateFromError(error) runs during the render phase when a child throws. It is a static method that receives the error and returns a state update ({ hasError: true }). Because it runs during rendering, it must be a pure function with no side effects. Its sole job is to flip the error flag so the boundary renders the fallback on the same render.
componentDidCatch(error, info) runs after the commit phase (the equivalent of useEffect for class components). It receives the error and an info object containing componentStack, a string of the component tree where the error originated. This is the right place for side effects: logging to Sentry, sending to an analytics service, or calling an error reporting API. Do not update state here; use getDerivedStateFromError for that.
What Error Boundaries Do Not Catch
Error boundaries only catch errors that occur during rendering, in lifecycle methods, and in constructors of child class components. They do not catch: errors in event handlers (use try/catch inside the handler), errors in async code (setTimeout, fetch callbacks, async event handlers), errors in SSR, and errors thrown by the error boundary itself.
Isolation Strategy
The most effective use of error boundaries is isolation. Wrapping the entire application in a single boundary means any error brings down all visible UI and shows one error screen. Wrapping independent sections (a chart widget, a sidebar, a payment form, a recommendation panel) means each section can fail independently while the rest of the page remains functional.
Key Code Explained
import React from 'react';
interface ErrorBoundaryProps {
children: React.ReactNode;
fallback?: React.ReactNode; // custom fallback UI
onError?: (error: Error, info: React.ErrorInfo) => void; // logging callback
}
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
}
// Render phase: return state update to show fallback
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error };
}
// Commit phase: safe to log to error services
componentDidCatch(error: Error, info: React.ErrorInfo) {
console.error('ErrorBoundary caught:', error);
console.error('Component stack:', info.componentStack);
// Call optional logging callback from parent
this.props.onError?.(error, info);
}
// Retry: reset state so children re-render from a clean slate
private handleReset = () => {
this.setState({ hasError: false, error: null });
};
render() {
if (this.state.hasError) {
// Render custom fallback if provided, otherwise default
if (this.props.fallback) {
return this.props.fallback;
}
return (
<div className="error-card" role="alert">
<h2>Something went wrong</h2>
<p className="error-message">{this.state.error?.message}</p>
<button onClick={this.handleReset}>Try again</button>
</div>
);
}
return this.props.children;
}
}
// Usage: isolate independent sections
function Dashboard() {
return (
<DashboardLayout>
<Sidebar />
<main>
<StatsBar />
{/* Each section isolated: one failure does not affect others */}
<ErrorBoundary
fallback={<p>Chart failed to load.</p>}
onError={(error) => Sentry.captureException(error)}
>
<RevenueChart />
</ErrorBoundary>
<ErrorBoundary
fallback={<p>Activity feed unavailable.</p>}
>
<RecentActivity />
</ErrorBoundary>
<ErrorBoundary
fallback={<p>Recommendations unavailable.</p>}
>
<RecommendationPanel />
</ErrorBoundary>
</main>
</DashboardLayout>
);
}
// react-error-boundary: function component wrapper (third-party library)
import { ErrorBoundary as REB } from 'react-error-boundary';
function FallbackComponent({
error,
resetErrorBoundary,
}: {
error: Error;
resetErrorBoundary: () => void;
}) {
return (
<div role="alert">
<p>Error: {error.message}</p>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
function MyFeature() {
return (
<REB FallbackComponent={FallbackComponent}>
<RiskyComponent />
</REB>
);
}
// react-error-boundary also provides useErrorBoundary() hook
// for programmatically throwing errors caught by the nearest boundary
The handleReset method resets hasError to false, which causes React to re-render the children from scratch. If the error was transient (a momentary network issue that caused a failed prop), the retry may succeed. If the error is deterministic (a component always throws given the same props), the user will see the error again immediately. The retry button should only be shown when the error might be recoverable.
Tradeoffs
| Approach | Function component | Logging support | Custom fallback | Retry support | Effort |
|---|---|---|---|---|---|
| Manual class ErrorBoundary | No (class only) | Yes | Yes | Yes (setState) | Medium |
| react-error-boundary | Yes (wraps class) | Yes | Yes | Yes (resetErrorBoundary) | Low |
What Interviewers Actually Check
- Whether you know
getDerivedStateFromError(render phase) vscomponentDidCatch(commit phase) - Whether you know error boundaries must be class components
- Whether you know what they do and do not catch
- Whether you wrap independent sections separately, not the whole app
- Whether you know
react-error-boundaryas the practical alternative
Follow-Up Questions
- How would you use
useErrorBoundary()fromreact-error-boundaryto programmatically throw into the nearest boundary from a hook? - How would you integrate Sentry error reporting with an error boundary and include the component stack?
- What is the difference between an error boundary and a try/catch block in terms of what they can protect against?
- How would you test an error boundary using React Testing Library by forcing a child to throw?
- In React 19, is there any new support for error boundaries in function components?
Common Candidate Mistakes
- Thinking error boundaries catch errors in event handlers: they do not. Try/catch inside the handler is required.
- Using
componentDidCatchto callsetState, which updates on the wrong lifecycle phase (usegetDerivedStateFromError) - Wrapping the entire application in a single error boundary instead of isolating independent sections
- Not providing a retry button, forcing users to do a full page reload on transient errors
- Not knowing
react-error-boundaryand thinking error boundaries require writing class components from scratch every time
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you implement a class
ErrorBoundarywithgetDerivedStateFromErrorandcomponentDidCatch? - Can you explain the difference:
getDerivedStateFromErrorruns during render to update state;componentDidCatchruns after commit for side effects? - Can you list four things error boundaries do not catch?
- Can you explain why wrapping independent sections is better than wrapping the whole app?
- Can you implement a retry button that resets the error state?
Summary
An error boundary is a class component that implements getDerivedStateFromError and optionally componentDidCatch. When any child throws during render, getDerivedStateFromError runs synchronously during the render phase, returns { hasError: true }, and the boundary renders its fallback UI instead of the children on the same render. componentDidCatch runs after the commit phase and is the correct place for logging to error services.
Error boundaries only catch synchronous render and lifecycle errors. They do not catch errors in event handlers (use try/catch inside the handler), async code, SSR, or errors thrown by the boundary itself. They cannot be implemented as function components because there are no hook equivalents for these lifecycle methods. The react-error-boundary library wraps the class in a clean API and adds a useErrorBoundary() hook for programmatic boundary triggering.
Wrap independent UI sections separately rather than the entire app. A single boundary means one failure blanks the entire page. Independent boundaries mean a broken chart widget shows its own fallback while the rest of the dashboard remains functional.
Can I use error boundaries in function components?
Not directly. Error boundary behavior requires getDerivedStateFromError and componentDidCatch, which are class component lifecycle methods with no hook equivalents. Use the react-error-boundary library which wraps the class for you and provides an ErrorBoundary component with a useErrorBoundary hook.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement