Error Boundaries
One bug in one widget should not turn the whole page white. Error boundaries catch errors in their part of the tree and show a fallback, while the rest of the app keeps working.
On this page · 6 min · Advanced
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 6 min
- Difficulty
- Advanced
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Explain what error boundaries catch
- Write a class error boundary
- Use react-error-boundary with reset
- Place boundaries at sensible levels
Explanation#
If a component throws while rendering, React unmounts the whole tree — users see a blank page. An error boundary stops that at a chosen point.
An error boundary is a class component with static getDerivedStateFromError (to show the fallback) and componentDidCatch (to log the error). There is no hook equivalent yet.
Most projects use the react-error-boundary package: <ErrorBoundary FallbackComponent={...} onReset={...}> with a resetErrorBoundary function for “Try again”.
Boundaries catch errors in rendering, lifecycle methods and constructors below them. They do NOT catch errors in event handlers, async code (setTimeout, fetch promises) or the boundary itself — handle those with try/catch and error state.
Place one boundary around the whole app (last resort) and others around independent widgets and routes, so a broken chart does not take down navigation.
Syntax#
class ErrorBoundary extends Component {
state = { error: null };
static getDerivedStateFromError(error) { return { error }; }
componentDidCatch(error, info) { logError(error, info.componentStack); }
render() { return this.state.error ? this.props.fallback : this.props.children; }
}
Code example: A broken widget that does not break the page#
import { Component, useState } from 'react';
class ErrorBoundary extends Component {
state = { error: null };
static getDerivedStateFromError(error) {
return { error }; // switch to the fallback
}
componentDidCatch(error, info) {
console.error('Widget crashed:', error.message, info.componentStack); // send to logging here
}
reset = () => this.setState({ error: null });
render() {
if (this.state.error) {
return (
<div role="alert" style={{ border: '1px solid #dc2626', padding: 8 }}>
<p>{this.props.title} could not be displayed.</p>
<button onClick={this.reset}>Try again</button>
</div>
);
}
return this.props.children;
}
}
function RevenueWidget({ data }) {
// Bug: crashes when data is missing
return <p>Revenue: ₹{data.total.toLocaleString('en-IN')}</p>;
}
export default function Dashboard() {
const [broken, setBroken] = useState(true);
return (
<>
<nav>Home · Orders · Reports</nav>
<ErrorBoundary title="Revenue">
<RevenueWidget data={broken ? null : { total: 125000 }} />
</ErrorBoundary>
<ErrorBoundary title="Orders">
<p>Orders today: 42</p>
</ErrorBoundary>
<button onClick={() => setBroken(false)}>Fix the data</button>
</>
);
}
The navigation and “Orders today: 42” render normally. The revenue widget shows a red box “Revenue could not be displayed.” and the console logs “Widget crashed: Cannot read properties of null (reading ‘total’)”. Clicking “Fix the data” then “Try again” shows “Revenue: ₹1,25,000”.
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- RevenueWidget throws while rendering because data is null.
- getDerivedStateFromError stores the error, so the boundary renders its fallback.
- componentDidCatch logs the error and component stack.
- Only the Revenue boundary is affected; navigation and the Orders widget keep working.
- reset clears the error so the children render again once the data is fixed.
Real-world use case#
A news site wraps each homepage section in a boundary. When a third-party weather widget starts throwing, only that box shows “Weather unavailable”, and the error is reported to Sentry.
Common mistakes#
Errors thrown in onClick or in a fetch promise are not caught. Use try/catch and error state there.
Any error replaces the entire app with the fallback. Add boundaries around independent sections.
Fallbacks hide errors from developers. Report them in componentDidCatch (or onError).
Best practices#
- A top-level boundary plus boundaries per widget or route
- Log errors to a monitoring service
- Offer a retry or reload
- Use react-error-boundary in real projects
Remember this#
Boundary = try/catch for rendering.
Not caught: event handlers, async code, the boundary itself.
Small boundaries keep the rest of the page alive.
Practice exercise#
Your task: Write a Profile component that throws if user.name is missing, and wrap it in an ErrorBoundary with fallback text "Profile unavailable". Render it with a user that has no name.
Need a hint?
Show solution
import { Component } from 'react';
class Boundary extends Component {
state = { failed: false };
static getDerivedStateFromError() { return { failed: true }; }
render() { return this.state.failed ? <p role="alert">{this.props.fallback}</p> : this.props.children; }
}
function Profile({ user }) {
if (!user.name) throw new Error('User has no name');
return <h2>{user.name}</h2>;
}
export default function App() {
return (
<Boundary fallback="Profile unavailable">
<Profile user={{}} />
</Boundary>
);
}
Mini quiz#
Which error is caught by an error boundary?
Want more? Practise all React MCQs · React interview questions
My notes#
Saved in this browser.