NetcaneTechnologies

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.

Yasir Haleem2 min read

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.

About the author

Yasir Haleem is founder and lead engineer at Netcane Technologies. He builds production Next.js sites with headless CMS platforms — Strapi, Contentful, Sanity, and WordPress — with a focus on performance, SEO, and maintainable architecture.

Let's work together

Tell us about your project. We respond within one business day with a clear scope, timeline, and estimate.