Without special handling, one thrown error during rendering unmounts a React app's entire tree. Error Boundaries contain that damage to a specific section. This lesson covers why they're still class-based, strategic placement, what they don't catch, and the react-error-boundary library.
1One Bad Render Shouldn't Kill the Whole App
Without special handling, a thrown error anywhere during rendering unmounts the entire React tree, producing a blank screen even when the crash originated in one small, unimportant widget. Error Boundaries exist specifically to contain that damage to just the broken section.
2Error Boundaries Must Be Class Components
This remains the one place in modern React requiring a class component, since there's no hook equivalent for catching render errors. An Error Boundary implements static getDerivedStateFromError to render a fallback, and optionally componentDidCatch to log the error.
3Placing Boundaries Strategically
A single Error Boundary wrapping an entire app means any error anywhere blanks the whole UI. Wrapping individual, independent sections — a sidebar widget, a chart — with their own boundaries means a crash in one section doesn't take down the rest of the page.
4What Error Boundaries Do NOT Catch
Error Boundaries only catch errors during rendering, in lifecycle methods, and in constructors of the tree below them. They don't catch errors in event handlers, asynchronous code like a .then() callback, server-side rendering, or errors thrown in the boundary itself — those require regular try/catch handling.
5The react-error-boundary Library
Since hand-writing the class boilerplate for every boundary is repetitive, most teams use the react-error-boundary package, providing a ready-made ErrorBoundary component with a FallbackComponent prop, a resetErrorBoundary function for retry buttons, and an onError callback.
6Step-by-Step Breakdown
One Bad Render Shouldn't Kill the Whole App. Without special handling, a thrown error anywhere during rendering unmounts your ENTIRE React tree, leaving a blank white screen — even if the crash happened in one small, unimportant widget. Error Boundaries exist to contain that damage to just the broken part.
Error Boundaries Must Be Class Components. This is the one place in modern React where a class component is still required — there's no hook equivalent for catching render errors. An Error Boundary implements static getDerivedStateFromError (to render a fallback) and optionally componentDidCatch (to log the error).
Why is an Error Boundary implemented as a class component in modern React, rather than a hook?
- →There is currently no hook equivalent for catching render errors
- →Class components execute noticeably faster than function components
Placing Boundaries Strategically. A single Error Boundary wrapping your entire app is better than nothing, but it means ANY error anywhere blanks the whole UI. Wrapping individual, independent sections — a sidebar widget, a chart, a comments section — with their own boundaries means a crash in one doesn't take down the rest of the page.
What Error Boundaries Do NOT Catch. Error Boundaries only catch errors during rendering, in lifecycle methods, and in constructors of the tree below them. They do NOT catch errors in event handlers, async code (like a .then() callback), server-side rendering, or errors thrown in the boundary itself — those need regular try/catch.
Does an Error Boundary catch an error thrown inside a button's onClick handler?
- →No — Error Boundaries only catch errors during rendering, not in event handlers
- →Yes — Error Boundaries catch every kind of thrown error, anywhere
The react-error-boundary Library. Since writing the class boilerplate for every boundary gets repetitive, most teams use the react-error-boundary package, which provides a ready-made <ErrorBoundary> component with a FallbackComponent prop, a resetErrorBoundary function for 'try again' buttons, and an onError callback.
Mastery Achieved. You now understand Error Boundaries: why they're still class-based, containing crashes to specific page sections instead of the whole app, exactly what they do and don't catch, and using react-error-boundary to skip the boilerplate. Next, you'll learn how to design good loading states for the async work that hasn't errored — just hasn't finished yet.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Fallback UI Needs to Be Announced, Not Just Visible
When an Error Boundary's fallback replaces broken content, wrap it in a live region (role='alert' or aria-live='assertive') so screen reader users are informed a failure occurred, rather than silently encountering a different UI with no explanation.
SEO Implications
- 1
A Well-Placed Boundary Prevents a Fully Blank, Uncrawlable Page
Isolating a crash to one widget instead of unmounting the entire page keeps the rest of the page's content intact and crawlable, rather than a single component failure taking down an entire page's indexable content.
Best Practices
Wrap Independent, Non-Critical Sections Separately
Give widgets, charts, and other self-contained sections their own Error Boundary so a failure in one doesn't cascade into unmounting the rest of an otherwise-functional page.
Always Log Caught Errors to a Monitoring Service
Use componentDidCatch (or react-error-boundary's onError) to report caught errors to an error tracking service — silently swallowing errors with only a fallback UI hides real production bugs from the team.
Frequent Bugs
An error thrown inside a fetch's .then() callback isn't caught by a surrounding Error Boundary.
Error Boundaries don't catch asynchronous errors. Handle the promise rejection with a .catch() or a try/catch around an async/await call, and manage the resulting error state manually (e.g. with a state variable driving an error UI).
The whole app goes blank after a single small widget crashes.
There's likely only one Error Boundary wrapping the entire app, or none at all. Add more granular boundaries around independent sections so a crash in one widget doesn't unmount unrelated parts of the page.
Real-World Examples
Isolating a Third-Party Widget with Its Own Boundary
A dashboard embeds a third-party analytics widget whose code the team doesn't fully control and that occasionally throws unexpected errors. Wrapping just that widget in its own ErrorBoundary with a small 'Widget unavailable' fallback ensures the rest of the dashboard keeps working normally even if that specific widget crashes.
<ErrorBoundary FallbackComponent={() => <p>Widget unavailable</p>} onError={logError}>
<ThirdPartyAnalyticsWidget />
</ErrorBoundary>