Error Boundaries
In this page:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
// log the error
}
render() {
return this.state.hasError ? <p>Fallback UI</p> : this.props.children;
}
}
<ErrorBoundary><Component /></ErrorBoundary>
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.
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.
- Keep getDerivedStateFromError focused purely on updating state
- Use componentDidCatch for side effects like logging.
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.
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