Introduction
By default, a JavaScript error thrown anywhere in a component's render method, lifecycle method, or constructor unmounts the entire React component tree, resulting in a blank screen. Error boundaries solve this by catching errors in their child component tree, logging them, and displaying a fallback UI instead of crashing the whole application. As of React 18, error boundaries must be implemented as class components — there is no hook equivalent, because the underlying lifecycle methods (getDerivedStateFromError and componentDidCatch) have no hook counterparts.
Cricket analogy: Normally a fielder collapsing mid-match with injury could force a forfeit; an error boundary is like a reserve-player rule that catches the collapse and substitutes a fallback player instead of forfeiting — but only a class-based captain role can invoke this rule, not a regular player (hooks).
Syntax
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render shows the fallback UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Log the error to an error reporting service
console.error('Caught by ErrorBoundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h2>Something went wrong.</h2>;
}
return this.props.children;
}
}Explanation
getDerivedStateFromError is a static lifecycle method called during the 'render' phase after a descendant throws; it receives the error and returns a state update, which React uses to re-render with a fallback UI. componentDidCatch is called during the 'commit' phase and is the right place for side effects like logging the error to a service such as Sentry — it receives both the error and componentStack info describing which component threw. An error boundary only catches errors from its children's rendering, lifecycle methods, and constructors — it does NOT catch errors in event handlers, asynchronous code (setTimeout, promises), server-side rendering, or errors thrown in the boundary itself.
Cricket analogy: getDerivedStateFromError is like the third umpire reviewing a decision during play (render phase) and signaling a fallback ruling; componentDidCatch is like the match referee filing the incident report afterward (commit phase) — but neither catches a spectator's outburst (event handler) or a rain delay (async), only errors from the players (children).
Error boundaries cannot currently be written as function components using hooks — React does not provide a useErrorBoundary or equivalent hook as of React 18, since getDerivedStateFromError and componentDidCatch have no hook-based equivalents. To use error boundaries in a hooks-based codebase, you either write a small dedicated class component (often reused as a wrapper) or use a well-tested community library such as react-error-boundary.
Example
function BuggyCounter() {
const [count, setCount] = useState(0);
if (count === 3) {
throw new Error('Count reached 3, crashing on purpose!');
}
return (
<button onClick={() => setCount((c) => c + 1)}>
Count: {count}
</button>
);
}
function App() {
return (
<ErrorBoundary>
<BuggyCounter />
</ErrorBoundary>
);
}Output
Clicking the button increments the count normally for 0, 1, and 2. On the third click, count becomes 3, BuggyCounter throws during render, ErrorBoundary catches it via getDerivedStateFromError and componentDidCatch, logs the error to the console, and the UI switches from the button to the fallback message 'Something went wrong.' instead of the whole app crashing to a blank page.
Cricket analogy: A batter safely completes singles on balls one and two, but on the third ball attempts an ambitious ramp shot and gets bowled; getDerivedStateFromError 'catches' the dismissal, componentDidCatch logs it to the scorebook, and the broadcast switches to a 'Wicket!' graphic instead of the feed cutting out.
Key Takeaways
- Error boundaries catch JavaScript errors during rendering, in lifecycle methods, and in constructors of their child tree, preventing a full app crash.
- As of React 18, error boundaries must be class components using getDerivedStateFromError and/or componentDidCatch — there is no hooks equivalent.
- getDerivedStateFromError updates state to render a fallback UI; componentDidCatch is for side effects like logging.
- Error boundaries do NOT catch errors in event handlers, async code, or SSR — those need try/catch or other handling.
- It's common to wrap key parts of an app (e.g., routes or widgets) in separate error boundaries to isolate failures.
Practice what you learned
1. Which lifecycle methods are required to implement an error boundary in React?
2. As of React 18, how can error boundaries be implemented?
3. Which of the following errors will an error boundary NOT catch?
4. What is the purpose of componentDidCatch in an error boundary?
Was this page helpful?
You May Also Like
Testing React Components
Use React Testing Library to write user-focused tests that render components and query the DOM like a real user.
Class Components in React
Learn the legacy class-based approach to building React components, including lifecycle methods and state.
Common React Pitfalls
Frequent React mistakes — direct state mutation, bad dependency arrays, index keys, and more — with fixes.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics