React's default error behavior is brutal and most teams never notice until a customer shows them a screenshot. One thrown error anywhere in the tree, and React unmounts the whole thing. White page. No header, no navigation, no way for the user to recover except leaving.
Error boundaries exist to contain that blast radius, and they are the rare React feature that is older than most of the codebases missing them.
What they catch, and what they don't
Boundaries catch errors thrown during rendering, inside lifecycle methods, and in constructors. They do not catch event handlers, code in setTimeout or promises, server component errors, or errors thrown inside the boundary itself.
That gap list is why some developers conclude boundaries are useless. It is backwards. Render-time failures, a field that is undefined on the third API response, a third-party component that throws on a bad prop, are exactly the failures that take down whole pages in production, and they are the ones nothing else catches.
The one boundary you need
A class component, about 40 lines: getDerivedStateFromError flips state to show the fallback, componentDidCatch forwards the error and component stack to monitoring, the fallback renders a short message and a Try again button that resets the state.
You write it once. Every team that ships without one is choosing, per feature, that a crash there should blank the page. Nobody actually chooses that. It just never came up.
Where to put them
Scope to risk, not to the component tree. The question is not "how deep should boundaries go" but "what breaks the page when this fails":
- Charts and dashboards fed by API data
- Feeds, comments, recommendations, anything user generated
- Third-party embeds: maps, chat widgets, video players
- Anything that fetches during render
If the product can survive without the widget showing, the widget gets its own boundary. The checkout form does not die because the product carousel threw.
The fallback needs an exit
A fallback that says "something went wrong" with no action is a white page with better branding. Give every fallback a Try again button that clears the error state, and set resetKeys so navigating away clears it automatically. Users click retry far more often than they click away.
Without logging, it's just a prettier crash
componentDidCatch hands you the error and the component stack. Send both to your monitoring tool with the route and release version attached. The boundary turns a customer screenshot into a stack trace, which is the entire operational argument for shipping them.
Add one root boundary as the safety net today, then start scoping them into the risky widgets. If you are not sure where your app would blank right now, that is the tell that the answer is "everywhere".