Documentation
Error Handling
Error Boundary

Error Boundary

Catching and handling React component errors.

Basic Error Boundary

import { Component, type ErrorInfo, type ReactNode } from 'react';
 
interface Props {
  children: ReactNode;
  fallback?: ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}
 
interface State {
  hasError: boolean;
  error: Error | null;
}
 
export class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null };
  }
 
  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }
 
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
    this.props.onError?.(error, errorInfo);
  }
 
  render() {
    if (this.state.hasError) {
      return this.props.fallback ?? <DefaultErrorFallback error={this.state.error} />;
    }
 
    return this.props.children;
  }
}

Default Fallback UI

interface ErrorFallbackProps {
  error: Error | null;
  onReset?: () => void;
}
 
const DefaultErrorFallback = ({ error, onReset }: ErrorFallbackProps) => (
  <div className="flex min-h-[400px] flex-col items-center justify-center p-8">
    <div className="text-center">
      <h2 className="mb-2 text-xl font-semibold text-gray-900 dark:text-white">
        Something went wrong
      </h2>
      <p className="mb-4 text-gray-600 dark:text-gray-400">
        {error?.message ?? 'An unexpected error occurred'}
      </p>
      {onReset && (
        <button
          onClick={onReset}
          className="rounded-lg bg-orange-500 px-4 py-2 text-white hover:bg-orange-600"
        >
          Try Again
        </button>
      )}
    </div>
  </div>
);

Usage Patterns

Wrap Routes

// app/router/Router.tsx
const Router = () => (
  <Routes>
    <Route
      path="/dashboard"
      element={
        <ErrorBoundary fallback={<PageError />}>
          <Dashboard />
        </ErrorBoundary>
      }
    />
  </Routes>
);

Wrap Feature Sections

const Dashboard = () => (
  <div className="grid gap-4">
    <ErrorBoundary fallback={<WidgetError />}>
      <StatsWidget />
    </ErrorBoundary>
 
    <ErrorBoundary fallback={<WidgetError />}>
      <ChartWidget />
    </ErrorBoundary>
 
    <ErrorBoundary fallback={<WidgetError />}>
      <ActivityFeed />
    </ErrorBoundary>
  </div>
);

With Error Reporting

const AppErrorBoundary = ({ children }: { children: ReactNode }) => (
  <ErrorBoundary
    onError={(error, errorInfo) => {
      // Send to error tracking service
      errorReportingService.captureException(error, {
        extra: { componentStack: errorInfo.componentStack },
      });
    }}
    fallback={<AppCrashScreen />}
  >
    {children}
  </ErrorBoundary>
);

React Error Boundary Library

Using react-error-boundary for more features:

import { ErrorBoundary } from 'react-error-boundary';
 
const ErrorFallback = ({
  error,
  resetErrorBoundary,
}: {
  error: Error;
  resetErrorBoundary: () => void;
}) => (
  <div role="alert">
    <p>Something went wrong:</p>
    <pre>{error.message}</pre>
    <button onClick={resetErrorBoundary}>Try again</button>
  </div>
);
 
const App = () => (
  <ErrorBoundary
    FallbackComponent={ErrorFallback}
    onReset={() => {
      // Reset app state
    }}
    resetKeys={[userId]}
  >
    <Dashboard />
  </ErrorBoundary>
);

Error Boundary Limitations

Error boundaries do NOT catch:

  • Event handler errors
  • Async code (setTimeout, promises)
  • Server-side rendering errors
  • Errors in the boundary itself

For these, use try-catch or .catch():

const handleClick = async () => {
  try {
    await submitForm();
  } catch (error) {
    toast.error('Failed to submit form');
  }
};