How to Create an Error Boundary in React

Advanced10 min interview
Skills tested:
Implementing a class component with getDerivedStateFromError and componentDidCatchDistinguishing getDerivedStateFromError (render phase, updates state) from componentDidCatch (commit phase, for logging)Knowing what error boundaries do and do not catchWrapping independent page sections in separate error boundaries for isolationAdding a retry mechanism that resets hasError state

Advertisement

🧩 Scenario

Error boundaries prevent a single broken component from unmounting the entire React tree. They are required knowledge for production React applications: a chart with malformed data, a third-party widget that throws, or a component with a missing prop should show a fallback, not crash the whole page. This question tests whether you know the class component pattern and its limitations.

Architecture Walkthrough

Two Lifecycle Methods, Two Responsibilities

An error boundary implements two class lifecycle methods. getDerivedStateFromError(error) runs during the render phase when a child throws. It is a static method that receives the error and returns a state update ({ hasError: true }). Because it runs during rendering, it must be a pure function with no side effects. Its sole job is to flip the error flag so the boundary renders the fallback on the same render.

componentDidCatch(error, info) runs after the commit phase (the equivalent of useEffect for class components). It receives the error and an info object containing componentStack, a string of the component tree where the error originated. This is the right place for side effects: logging to Sentry, sending to an analytics service, or calling an error reporting API. Do not update state here; use getDerivedStateFromError for that.

What Error Boundaries Do Not Catch

Error boundaries only catch errors that occur during rendering, in lifecycle methods, and in constructors of child class components. They do not catch: errors in event handlers (use try/catch inside the handler), errors in async code (setTimeout, fetch callbacks, async event handlers), errors in SSR, and errors thrown by the error boundary itself.

Isolation Strategy

The most effective use of error boundaries is isolation. Wrapping the entire application in a single boundary means any error brings down all visible UI and shows one error screen. Wrapping independent sections (a chart widget, a sidebar, a payment form, a recommendation panel) means each section can fail independently while the rest of the page remains functional.


Key Code Explained

import React from 'react';

interface ErrorBoundaryProps {
  children: React.ReactNode;
  fallback?: React.ReactNode;           // custom fallback UI
  onError?: (error: Error, info: React.ErrorInfo) => void; // logging callback
}

interface ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  // Render phase: return state update to show fallback
  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }

  // Commit phase: safe to log to error services
  componentDidCatch(error: Error, info: React.ErrorInfo) {
    console.error('ErrorBoundary caught:', error);
    console.error('Component stack:', info.componentStack);

    // Call optional logging callback from parent
    this.props.onError?.(error, info);
  }

  // Retry: reset state so children re-render from a clean slate
  private handleReset = () => {
    this.setState({ hasError: false, error: null });
  };

  render() {
    if (this.state.hasError) {
      // Render custom fallback if provided, otherwise default
      if (this.props.fallback) {
        return this.props.fallback;
      }

      return (
        <div className="error-card" role="alert">
          <h2>Something went wrong</h2>
          <p className="error-message">{this.state.error?.message}</p>
          <button onClick={this.handleReset}>Try again</button>
        </div>
      );
    }

    return this.props.children;
  }
}


// Usage: isolate independent sections
function Dashboard() {
  return (
    <DashboardLayout>
      <Sidebar />

      <main>
        <StatsBar />

        {/* Each section isolated: one failure does not affect others */}
        <ErrorBoundary
          fallback={<p>Chart failed to load.</p>}
          onError={(error) => Sentry.captureException(error)}
        >
          <RevenueChart />
        </ErrorBoundary>

        <ErrorBoundary
          fallback={<p>Activity feed unavailable.</p>}
        >
          <RecentActivity />
        </ErrorBoundary>

        <ErrorBoundary
          fallback={<p>Recommendations unavailable.</p>}
        >
          <RecommendationPanel />
        </ErrorBoundary>
      </main>
    </DashboardLayout>
  );
}


// react-error-boundary: function component wrapper (third-party library)
import { ErrorBoundary as REB } from 'react-error-boundary';

function FallbackComponent({
  error,
  resetErrorBoundary,
}: {
  error: Error;
  resetErrorBoundary: () => void;
}) {
  return (
    <div role="alert">
      <p>Error: {error.message}</p>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

function MyFeature() {
  return (
    <REB FallbackComponent={FallbackComponent}>
      <RiskyComponent />
    </REB>
  );
}
// react-error-boundary also provides useErrorBoundary() hook
// for programmatically throwing errors caught by the nearest boundary

The handleReset method resets hasError to false, which causes React to re-render the children from scratch. If the error was transient (a momentary network issue that caused a failed prop), the retry may succeed. If the error is deterministic (a component always throws given the same props), the user will see the error again immediately. The retry button should only be shown when the error might be recoverable.


Tradeoffs

ApproachFunction componentLogging supportCustom fallbackRetry supportEffort
Manual class ErrorBoundaryNo (class only)YesYesYes (setState)Medium
react-error-boundaryYes (wraps class)YesYesYes (resetErrorBoundary)Low

What Interviewers Actually Check

  • Whether you know getDerivedStateFromError (render phase) vs componentDidCatch (commit phase)
  • Whether you know error boundaries must be class components
  • Whether you know what they do and do not catch
  • Whether you wrap independent sections separately, not the whole app
  • Whether you know react-error-boundary as the practical alternative

Follow-Up Questions

  1. How would you use useErrorBoundary() from react-error-boundary to programmatically throw into the nearest boundary from a hook?
  2. How would you integrate Sentry error reporting with an error boundary and include the component stack?
  3. What is the difference between an error boundary and a try/catch block in terms of what they can protect against?
  4. How would you test an error boundary using React Testing Library by forcing a child to throw?
  5. In React 19, is there any new support for error boundaries in function components?

Common Candidate Mistakes

  • Thinking error boundaries catch errors in event handlers: they do not. Try/catch inside the handler is required.
  • Using componentDidCatch to call setState, which updates on the wrong lifecycle phase (use getDerivedStateFromError)
  • Wrapping the entire application in a single error boundary instead of isolating independent sections
  • Not providing a retry button, forcing users to do a full page reload on transient errors
  • Not knowing react-error-boundary and thinking error boundaries require writing class components from scratch every time

Interview Readiness Checklist

Before you leave this question, make sure you can answer:

  • Can you implement a class ErrorBoundary with getDerivedStateFromError and componentDidCatch?
  • Can you explain the difference: getDerivedStateFromError runs during render to update state; componentDidCatch runs after commit for side effects?
  • Can you list four things error boundaries do not catch?
  • Can you explain why wrapping independent sections is better than wrapping the whole app?
  • Can you implement a retry button that resets the error state?

Summary

An error boundary is a class component that implements getDerivedStateFromError and optionally componentDidCatch. When any child throws during render, getDerivedStateFromError runs synchronously during the render phase, returns { hasError: true }, and the boundary renders its fallback UI instead of the children on the same render. componentDidCatch runs after the commit phase and is the correct place for logging to error services.

Error boundaries only catch synchronous render and lifecycle errors. They do not catch errors in event handlers (use try/catch inside the handler), async code, SSR, or errors thrown by the boundary itself. They cannot be implemented as function components because there are no hook equivalents for these lifecycle methods. The react-error-boundary library wraps the class in a clean API and adds a useErrorBoundary() hook for programmatic boundary triggering.

Wrap independent UI sections separately rather than the entire app. A single boundary means one failure blanks the entire page. Independent boundaries mean a broken chart widget shows its own fallback while the rest of the dashboard remains functional.

Related Videos
Watch these videos to learn more about this topic
Frequently Asked Questions

Can I use error boundaries in function components?

Not directly. Error boundary behavior requires getDerivedStateFromError and componentDidCatch, which are class component lifecycle methods with no hook equivalents. Use the react-error-boundary library which wraps the class for you and provides an ErrorBoundary component with a useErrorBoundary hook.

Advertisement


Stay Updated

Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.

Advertisement