Error Boundaries
In this page:
What Error Boundaries Catch
Without an error boundary, a JavaScript error thrown anywhere during rendering unmounts the ENTIRE React component tree, showing a blank page. An error boundary catches errors thrown by its child components during rendering, lifecycle methods, and constructors, letting you show a fallback UI instead of a blank screen.
Note: Wrap risky or complex sections of your UI (like a widget rendering untrusted third-party data) in their own error boundary.
Warning: Error boundaries do NOT catch errors inside event handlers (like an onClick handler throwing) — those need regular try/catch instead.
Example: What Error Boundaries Catch
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
render() {
if (this.state.hasError) return <p>Something went wrong.</p>;
return this.props.children;
}
}
function BuggyComponent() { throw new Error("Oops"); }
function App() {
return <ErrorBoundary><BuggyComponent /></ErrorBoundary>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Logging Errors with componentDidCatch
Alongside getDerivedStateFromError (which updates state to show the fallback UI), componentDidCatch is a second lifecycle method that receives the actual error and additional info, commonly used to log the error to a monitoring service without affecting what's rendered.
Note: Keep getDerivedStateFromError focused purely on updating state; use componentDidCatch for side effects like logging.
Warning: componentDidCatch alone (without getDerivedStateFromError) won't update the UI to a fallback — you need both methods working together for the typical pattern.
Example: Logging Errors with componentDidCatch
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error, info) {
console.log("Logged error:", error.message);
}
render() {
if (this.state.hasError) return <p>Error logged and handled.</p>;
return this.props.children;
}
}
function BuggyComponent() { throw new Error("Simulated crash"); }
function App() {
return <ErrorBoundary><BuggyComponent /></ErrorBoundary>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Using Multiple, Scoped Error Boundaries
Instead of one giant error boundary around the whole app (which would blank out everything on any single error), wrapping smaller, independent sections in their own boundaries limits the impact — if a sidebar widget crashes, the rest of the page keeps working normally.
Note: Good boundary boundaries are natural feature boundaries: a comments section, a chart widget, a third-party embed — each independently recoverable.
Warning: A single app-wide error boundary means ANY error anywhere blanks the entire UI, defeating much of the benefit of using boundaries at all.
Example: Using Multiple, Scoped Error Boundaries
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
render() { return this.state.hasError ? <p>Widget failed to load.</p> : this.props.children; }
}
function BrokenWidget() { throw new Error("Widget crash"); }
function App() {
return (
<div>
<p>This part still works fine.</p>
<ErrorBoundary><BrokenWidget /></ErrorBoundary>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Trying to write an error boundary as a function component — as of React 18, error boundaries must be class components.
- Expecting an error boundary to catch errors in event handlers — it only catches errors during rendering, lifecycle methods, and constructors.
- Wrapping the entire app in just one error boundary, so any error takes down the whole UI instead of just the broken section.
- An error boundary is a class component that catches JavaScript errors in its child component tree during rendering.
- getDerivedStateFromError and componentDidCatch are the two lifecycle methods that implement this behavior.
- Error boundaries do NOT catch errors in event handlers, async code, or the boundary's own rendering.
- Wrapping smaller, independent sections of the UI in separate boundaries limits the blast radius of any one error.
Available since React 16.0 (error boundaries introduced); still require a class component as of React 18.
Chapter Quiz — Complete all 14 topics to unlock
0/14 topics done
Complete these topics first:
- Error Boundaries
- React Portals
- Modals using Portals (practical use)
- React Suspense
- Code Splitting with React.lazy
- Introduction to Server Components
- Introduction to Next.js (server-side React)
- Using React with TypeScript
- Scalable Folder Architecture
- Common React Design Patterns
- Component Documentation with Storybook
- Accessibility (a11y) in React
- i18n with react-i18next
- React Security Best Practices