React Error Boundaries
Catch React errors with error boundaries, handle lifecycle errors, and display fallback UI gracefully.
TL;DR
- 01Create a class component with getDerivedStateFromError to catch errors.
- 02Display fallback UI when errors are caught instead of a blank page.
- 03Log errors for debugging and monitoring in production.
Tips
- 01Use error boundaries at the page level and around features to keep the rest of your app working when something breaks.
Warnings
- 01Error boundaries only catch errors during rendering, not in event handlers or async code — use try-catch for those.
Creating Error Boundaries
- Create a class component with error lifecycle methods.
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1>; } return this.props.children; } } - Error boundaries only work with class components, not functions.
- Use
getDerivedStateFromErrorto update state when an error occurs. - This catches errors in the component tree below the boundary.
- Error boundaries must return a fallback UI or null.
Logging and Debugging
- Use
componentDidCatchto log errors for debugging.class ErrorBoundary extends React.Component { componentDidCatch(error, errorInfo) { console.error("Error caught:", error); console.error("Error info:", errorInfo.componentStack); // Send to error tracking service logErrorToService(error, errorInfo); } render() { if (this.state.hasError) { return <h1>Something went wrong</h1>; } return this.props.children; } } componentDidCatchis called after an error has been thrown.- Use it to log errors to services like Sentry or DataDog.
- Include the error stack and component tree information in logs.
- Never throw errors from
componentDidCatchitself.
What Error Boundaries Catch
- Error boundaries catch errors during rendering in child components.
// This error is caught function Child() { throw new Error("Oops"); } <ErrorBoundary> <Child /> {/* Error is caught here */} </ErrorBoundary> - They do NOT catch errors from event handlers or async code.
// This error is NOT caught <button onClick={() => { throw new Error("Oops"); }}> Click me </button> // Use try-catch for these instead <button onClick={() => { try { riskyOperation(); } catch (error) { // Handle error } }}> Safe click </button> - Error boundaries don't catch errors in the boundary itself.
Nested Error Boundaries
- Use multiple error boundaries to granularly handle errors.
<ErrorBoundary> <Header /> <ErrorBoundary> <MainContent /> </ErrorBoundary> <Sidebar /> </ErrorBoundary> - Errors in MainContent are caught by the inner boundary.
- Errors in Header or Sidebar are caught by the outer boundary.
- This lets you keep the rest of the app running when part fails.
- Use granular boundaries for user-facing features like widgets.
Error Boundary Patterns
- Show a user-friendly error message with recovery option.
class ErrorBoundary extends React.Component { render() { if (this.state.hasError) { return ( <div> <h1>Oops, something went wrong</h1> <p>Please try refreshing the page</p> <button onClick={() => window.location.reload()}> Reload Page </button> </div> ); } return this.props.children; } } - Create reusable error boundary components for common layouts.
<PageErrorBoundary> <PageContent /> </PageErrorBoundary> - Combine error boundaries with error tracking for production monitoring.
- Reset error state when user navigates to a new page.
FAQ
Define a class component that implements getDerivedStateFromError to update state when an error is caught, then return fallback UI in render when that state is set. You can also implement componentDidCatch to receive the error and info objects for logging.
Error boundaries only intercept errors thrown during the React render cycle — event handlers run outside that cycle, so you need a try-catch block inside the handler itself.
Yes, and it's the recommended approach — nest them so a broken widget or feature only unmounts its own subtree while the rest of the page stays functional. Placing one at the root acts as a last-resort safety net, not a replacement for granular boundaries.
Implement componentDidCatch(error, info) in your class component and call your logging service there — info.componentStack gives you the full component trace, which is invaluable for debugging production issues.
No — as of React 18, error boundaries must be class components because the required lifecycle methods (getDerivedStateFromError, componentDidCatch) have no hook equivalents. Libraries like react-error-boundary wrap this pattern into a reusable component so you can use it declaratively without writing the class yourself.