Error Boundaries and Recovery UX
Learn how React error boundaries work at the renderer level, how they differ from Suspense, and how to use them to design intentional recovery experiences instead of letting failures take down the entire app.
In production, failures don’t all mean the same thing. Some are temporary and recoverable, like a flaky network request, a short-lived timeout, or a rate limit that clears on retry. Others signal that the UI is no longer trustworthy, like corrupted data, a violated invariant, or an unexpected null value that breaks assumptions deeper in the tree. Treating both categories the same leads to bad outcomes: either you overreact (blanking the whole screen for a small issue) or you underreact (continuing to render a subtree that’s logically unsafe).
Before error boundaries, a render-time error could tear down a large part of the React tree. Many apps responded by showing a full-page error screen or telling the user to reload. That might be acceptable for a small app, but it doesn’t scale: it turns localized failures into total outages and forces users to lose context, navigation state, and in-progress work.
Error boundaries provide a safer option for React. They let the renderer catch errors in a specific subtree, commit a fallback UI for that region, and keep the rest of the app running. Unlike Suspense, error boundaries do not pause or retry rendering automatically; once an error is committed, React treats the subtree as unsafe until something changes in the tree. When it makes sense, error boundaries can also support explicit recovery flowsretry, reset, or navigate away without restarting the entire application.
Suspense protects temporal uncertainty and retries automatically. Error boundaries protect logical correctness and stop rendering until recovery is explicit.
Error Boundaries: Safe fallbacks and explicit recovery
React handles errors differently from suspension. If a component throws an error during rendering, React can’t safely continue rendering that subtree. Unlike Suspense, where React can pause and retry later, an error remains “failed” until something changes, such as new props, state, or a remount.
ErrorBoundary components create containment zones. When an error is thrown, React walks up the tree to find the nearest ErrorBoundary. That boundary becomes the decision point: React abandons the failed render and commits the boundary’s fallback UI instead, replacing only the broken subtree and keeping the rest of the app running.
The key difference is intent: a Suspense fallback means “not ready yet,” while an ErrorBoundary fallback means “cannot render safely.” React will not automatically retry an erroring subtree. Recovery requires an explicit change, such as a user retry, a reset of boundary state, or a remount.
When you combine ErrorBoundary and Suspense intentionally, you get two complementary safety tools: one for loading/latency and one for runtime failures. Together, they let the app degrade gracefully instead of collapsing. An error thrown during render does not partially apply and then get patched up, it invalidates the ...