Error Boundaries in Production

Contain rendering failures to a subtree, report them, and give users a way back — not a white screen.

An error boundary catches errors thrown while rendering its child tree, shows a fallback instead of unmounting your whole app, and gives you a hook to log the failure. In production this is the difference between one broken widget and a blank white page.

What they catch — and what they do not

  • Caught: errors during rendering, in lifecycle methods, and in constructors of the subtree below.
  • Not caught: errors in event handlers, in async code (setTimeout, promises), during SSR, and errors thrown in the boundary itself. Handle those with ordinary try/catch and by setting error state.

Still the one place classes remain

Error boundaries require getDerivedStateFromError (to render a fallback) and/or componentDidCatch (to log), which have no hook equivalent yet. Most teams write one small class — or use react-error-boundary for a friendly function API with a built-in reset.

<ErrorBoundary fallback={<p>Something broke.</p>}>
  <Widget />
</ErrorBoundary>

Design your boundaries deliberately

  • Granular, not global. Wrap each risky region — a chart, a third-party embed, a route — so one failure does not blank its neighbors.
  • Offer recovery. A Try again button that resets the boundary (often keyed on the current route or resource) beats forcing a full reload.
  • Report it. In componentDidCatch, send the error and component stack to your monitoring so you learn about failures users never report.

Pair boundaries with Suspense: Suspense owns loading, the boundary owns failed. Together they cover every outcome of async UI.

Example

Example · javascript
import { Component } from 'react';

class ErrorBoundary extends Component {
  state = { error: null };

  static getDerivedStateFromError(error) {
    return { error }; // render the fallback on next paint
  }

  componentDidCatch(error, info) {
    // Send to monitoring (Sentry, Datadog, your API...).
    reportError({ error, componentStack: info.componentStack });
  }

  reset = () => this.setState({ error: null });

  render() {
    if (this.state.error) {
      return (
        <div role="alert">
          <p>This section failed to load.</p>
          <button onClick={this.reset}>Try again</button>
        </div>
      );
    }
    return this.props.children;
  }
}

function reportError(payload) {
  console.error('Reported:', payload);
}

function Dashboard() {
  return (
    <ErrorBoundary>
      <RiskyChart />
    </ErrorBoundary>
  );
}

When to use it

  • Wrapping each dashboard widget in its own error boundary so a broken chart shows a 'Widget failed to load' fallback without taking down the rest of the dashboard.
  • A top-level error boundary that catches unexpected rendering failures, shows a friendly error page, and posts the error with component stack to Sentry.
  • Resetting an error boundary via a route-change key so users who navigate away from a broken page get a clean component instance on their next visit.

More examples

Minimal class error boundary

getDerivedStateFromError triggers the fallback UI and componentDidCatch fires side-effects like error logging, keeping rendering and reporting separate.

Example · js
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    reportError(error, info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback ?? <p>Something went wrong.</p>;
    }
    return this.props.children;
  }
}

Per-widget boundary with retry

Each widget gets its own boundary so one broken component shows a retry button while the rest of the dashboard continues to work.

Example · js
function WidgetShell({ id, children }) {
  return (
    <ErrorBoundary
      key={id}
      fallback={
        <div className="widget-error">
          <p>This widget failed to load.</p>
          <button onClick={() => window.location.reload()}>Retry</button>
        </div>
      }
    >
      {children}
    </ErrorBoundary>
  );
}

<WidgetShell id="revenue-chart"><RevenueChart /></WidgetShell>
<WidgetShell id="user-table"><UserTable /></WidgetShell>

Auto-reset boundary on navigation

Binding the boundary's key to the pathname forces React to remount it on navigation, automatically clearing any previous error state without manual reset logic.

Example · js
import { useLocation } from 'react-router-dom';

function App() {
  const location = useLocation();
  // key changes on every route change, discarding any error state
  return (
    <ErrorBoundary key={location.pathname}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </ErrorBoundary>
  );
}

Discussion

  • Be the first to comment on this lesson.