React
Error Boundaries and Resilient UI: Patterns for Production
How to use error boundaries so one failing component doesn’t take down the whole app—and how to recover.

When a component throws, React unmounts the tree unless an error boundary catches it. In production, you want boundaries in the right places and a clear path to recovery so users aren’t stuck on a blank screen.
Where to put error boundaries
Place boundaries at segment boundaries: around route-level content, around major features (dashboard, editor), or around third-party widgets. Don’t wrap every small component—one boundary per “fence” is enough. If the sidebar throws, the main content can still render; if the main content throws, the sidebar and shell stay up.
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Log to your service
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? (
<div role="alert">
Something went wrong.{" "}
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
);
}
return this.props.children;
}
}
// Usage
<ErrorBoundary fallback={<PageError onRetry={refetch} />}>
<Dashboard />
</ErrorBoundary>Use a fallback that matches the scope: a full-page message for the root, a small “This widget failed” for a single card. Include a retry or “reload” action when it makes sense.
What error boundaries don’t catch
Error boundaries only catch errors in the render phase and in lifecycle methods of children. They do not catch errors in event handlers, async code (setTimeout, fetch), or server components. Handle those with try/catch and local state (e.g. set an error state and show an inline message). For async boundaries, use a pattern that catches promise rejections and re-renders the boundary (e.g. triggering a state update that causes a throw in render).
Recovery and logging
Let users recover by resetting boundary state (e.g. “Try again” that sets hasError to false and re-mounts children) or by navigating away. Log errors in componentDidCatch to your monitoring service with context (route, user) so you can fix them. Avoid logging the same error repeatedly; aggregate or debounce if needed.
Summary
Put error boundaries at segment boundaries (routes, features, third-party widgets). Use a fallback that fits the scope and offer retry when possible. Remember that boundaries don’t catch event or async errors—handle those locally. With boundaries and logging in place, your UI stays resilient in production.