React

React Error Boundaries👨‍💻

A single uncaught error in one component can unmount your entire React tree and leave users staring at a blank screen. Error boundaries fix this by catching rendering errors in a subtree, showing a fallback UI, and keeping the rest of the app alive. They should be part of your component architecture from day one, not bolted on after the first production incident.

Key Takeaways

  • 1An error boundary is a class component that implements `static getDerivedStateFromError()` and/or `componentDidCatch()` -- there is no hook equivalent yet, not even in React 19
  • 2Error boundaries catch errors during rendering, in lifecycle methods, and in child constructors -- they do NOT catch errors in event handlers, async code (`setTimeout`, promises), or errors thrown inside the boundary itself
  • 3Strategic placement matters: a single boundary at the root gives you zero isolation. Wrap independent features (widgets, route sections, third-party integrations) in their own boundaries so one crash does not take everything down
  • 4The `componentDidCatch` method is for side effects like logging to Sentry or LogRocket; `getDerivedStateFromError` is for updating state so the next render shows the fallback UI
  • 5The `react-error-boundary` library provides a production-ready API with `FallbackComponent`, `resetKeys`, `onReset`, and the `useErrorBoundary` hook for triggering boundaries from event handlers and async code
  • 6Combine error boundaries with Suspense: Suspense handles the loading state, the error boundary handles the failure state -- together they cover the full async lifecycle

Master react error boundaries

Take the React Intermediate course with hands-on lessons and challenges.

Examples

Reusable ErrorBoundary component from scratch

tsx

A production-ready error boundary that supports static fallback nodes, render-prop fallbacks with access to the error and a reset function, and an onError callback for logging. This single component covers most use cases without any external dependency.

Error boundary with retry and Sentry reporting

tsx

Each dashboard widget gets its own error boundary. If the revenue chart crashes due to malformed API data, the user count and orders table keep working. The Sentry integration sends the React component stack alongside the error, which is critical for debugging -- without it you only get the JavaScript call stack, not which component tree path caused the failure.

Route-level boundary that resets on navigation

tsx

The resetKeys prop tells react-error-boundary to automatically clear the error state when the pathname changes. When the user navigates away and comes back, they get a fresh render instead of seeing a stale error. The onReset callback invalidates React Query caches so the retry fetches fresh data rather than replaying the same broken response.

Combining Suspense and ErrorBoundary for async data

tsx

Suspense handles the loading state, the error boundary handles the failure state. The ErrorBoundary wraps Suspense so that a rejected promise (network failure, 500, etc.) gets caught. This is the standard pattern for any async data section in a modern React app -- skipping either half means users see either a blank screen on error or no loading indicator.

Triggering error boundaries from event handlers

tsx

Error boundaries normally do not catch event handler errors. The useErrorBoundary hook from react-error-boundary bridges this gap by letting you programmatically push an error into the nearest boundary. This is especially useful for form submissions and other async operations where you want the same fallback UI and logging pipeline that your render errors already go through.

Nested boundaries with different granularity levels

tsx

Three levels of boundaries, each with a fallback that matches the scope of what broke. A chart crash shows a small 'Widget unavailable' placeholder. A route-level crash replaces the main content area but keeps the header and footer. An app-level crash is the last resort. Every boundary reports to Sentry, so you know about failures even when users never report them.

Common Mistakes

Mistake:

Placing a single error boundary at the root and calling it done -- any error in any component shows the same full-page fallback

Fix:

Use multiple boundaries at different levels: app-level as a catch-all, route-level to isolate pages, and widget-level to isolate independent features. The fallback UI should match the scope of what failed.

Mistake:

Expecting error boundaries to catch event handler errors, async code, or promise rejections -- then wondering why clicks and fetches still crash the app

Fix:

Error boundaries only catch errors during rendering, lifecycle methods, and constructors. For event handlers and async code, use `try/catch` directly, or use the `useErrorBoundary` hook from `react-error-boundary` to forward those errors into the nearest boundary.

Mistake:

Showing a fallback with no way to recover -- the user is stuck looking at 'Something went wrong' forever

Fix:

Always provide a recovery path: a retry button that resets the boundary state, a link to navigate away, or automatic reset via `resetKeys` tied to the current route. Combine retry with cache invalidation so the second attempt does not replay the same broken data.

Mistake:

Not logging errors in `componentDidCatch` -- errors are caught and silently swallowed, so the team never finds out about production crashes

Fix:

Connect `componentDidCatch` (or the `onError` prop in `react-error-boundary`) to your monitoring service. Send the error, the component stack trace, the current URL, and any relevant user context. This is the only way you will know what is actually breaking in production.

Best Practices

  • Treat error boundaries as architecture, not afterthought -- add them when you create the route layout, not after the first incident report. Every page should have at least one boundary wrapping its main content area.
  • Pair every Suspense boundary with an error boundary. Suspense handles the loading state; the error boundary handles the failure state. Skipping either half means users see a blank screen on network errors or no loading indicator at all.
  • Use the `react-error-boundary` library instead of writing your own class component in most cases. It provides `resetKeys` for automatic recovery, `useErrorBoundary` for event handler errors, and a clean `FallbackComponent` API that covers nearly every production scenario.
  • Match fallback complexity to what failed. A broken sidebar widget should show a small placeholder, not a full-page error. A broken checkout form should show a clear message with support contact info, not a generic 'try again'.
  • Test your error boundaries deliberately. Create a dev-only component that throws on render and wrap it in each boundary to verify the fallback UI, the logging, and the reset behavior all work before a real error forces you to find out.

Summary

Error boundaries catch rendering errors in a React component subtree and display a fallback UI instead of crashing the entire app. They require class components implementing `getDerivedStateFromError` and `componentDidCatch`, though the `react-error-boundary` library provides a much cleaner API. Place boundaries at multiple levels (app, route, widget) for proper isolation, always pair them with Suspense for async data, connect them to error monitoring, and provide retry or navigation options so users are never stuck on a dead screen.

Practice React with hands-on challenges

Learn react error boundaries hands-on in your IDE

Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.

Related Concepts

Related Cheatsheets

Master React with Stanza

Interactive lessons and challenges, right in your code editor.

Check the free courses. No credit card.