Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
openaec-foundation avatar

React Errors Boundaries

  • 12 installs
  • 6 repo stars
  • Updated July 8, 2026
  • openaec-foundation/react-claude-skill-package

Helps with frontend development tasks.

About

react-errors-boundaries is a Claude Code skill for frontend development. It helps solo builders move faster with AI-assisted development.

  • react-errors-boundaries
  • Frontend Development
  • AI-coding skill

React Errors Boundaries by the numbers

  • 12 all-time installs (skills.sh)
  • Ranked #1,643 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/react-claude-skill-package --skill react-errors-boundaries

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs12
repo stars6
Last updatedJuly 8, 2026
Repositoryopenaec-foundation/react-claude-skill-package

What it does

Helps with frontend development tasks.

Files

SKILL.mdMarkdownGitHub ↗

react-errors-boundaries

Quick Reference

Error Boundary Overview

AspectDetail
Component typeClass component ONLY (no hook equivalent exists)
CatchesErrors during rendering, lifecycle methods, constructors of child tree
Does NOT catchEvent handlers, async code, SSR errors, errors in the boundary itself
React 18getDerivedStateFromError + componentDidCatch
React 19Same API + onCaughtError/onUncaughtError callbacks on createRoot
Recommended libraryreact-error-boundary (function component wrapper with hooks)

Lifecycle Methods

MethodPhasePurposeSide Effects?
static getDerivedStateFromError(error)RenderReturn state update to show fallback UINO — must be pure
componentDidCatch(error, info)CommitLog errors to reporting serviceYES — side effects allowed

Critical Warnings

NEVER use a function component as an error boundary — React has NO hook equivalent for getDerivedStateFromError. Error boundaries MUST be class components or use the react-error-boundary library.

NEVER rely on error boundaries to catch event handler errors — ALWAYS use try/catch inside event handlers. Error boundaries only catch errors during rendering and lifecycle methods.

NEVER rely on error boundaries to catch async errors (promises, setTimeout) — ALWAYS use .catch() or try/catch in async code. The one exception: errors thrown inside startTransition callbacks ARE caught by error boundaries.

NEVER expect an error boundary to catch its own errors — if the boundary itself throws, a PARENT boundary must catch it. ALWAYS have a top-level boundary as a safety net.

NEVER use error boundaries as a substitute for proper input validation — error boundaries are a safety net for unexpected failures, not a control flow mechanism.

ALWAYS implement both getDerivedStateFromError AND componentDidCatch — the first renders fallback UI, the second logs the error. Using only one gives an incomplete error boundary.

ALWAYS provide a way to recover from errors — a "Try Again" button, navigation link, or key-based reset. A dead-end error screen frustrates users.

---

Complete TypeScript Error Boundary

import React, { Component, ErrorInfo, ReactNode } from 'react';

interface ErrorBoundaryProps {
  children: ReactNode;
  fallback: ReactNode | ((error: Error, reset: () => void) => ReactNode);
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}

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

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

  static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
    // Render phase — NO side effects. Return state for fallback UI.
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, info: ErrorInfo): void {
    // Commit phase — side effects allowed. Log to error service.
    console.error('Error boundary caught:', error);
    console.error('Component stack:', info.componentStack);
    this.props.onError?.(error, info);
  }

  private handleReset = (): void => {
    this.setState({ hasError: false, error: null });
  };

  render(): ReactNode {
    if (this.state.hasError && this.state.error) {
      if (typeof this.props.fallback === 'function') {
        return this.props.fallback(this.state.error, this.handleReset);
      }
      return this.props.fallback;
    }
    return this.props.children;
  }
}

export default ErrorBoundary;

Usage:

// Static fallback
<ErrorBoundary fallback={<p>Something went wrong.</p>}>
  <UserProfile userId={userId} />
</ErrorBoundary>

// Dynamic fallback with reset
<ErrorBoundary
  fallback={(error, reset) => (
    <div role="alert">
      <p>Error: {error.message}</p>
      <button onClick={reset}>Try Again</button>
    </div>
  )}
  onError={(error, info) => logToService(error, info)}
>
  <Dashboard />
</ErrorBoundary>

---

What Error Boundaries DO NOT Catch

ScenarioWhy Not CaughtUse Instead
Event handlersRun outside React render cycletry/catch in the handler
Async code (setTimeout, promises)Executes after render completes.catch() or try/catch
Server-side rendering (SSR)Different execution contextServer-side error handling
Errors in the boundary itselfCannot catch its own errorsA parent error boundary

Exception: Errors thrown inside startTransition callbacks ARE caught by error boundaries.

---

Boundary Placement Strategy

Decision Tree

Is this the app root?
├── YES → Page-level boundary (catch-all safety net)
│         Shows generic "Something went wrong" with reload option
│
└── NO → Is this an independent feature/widget?
    ├── YES → Feature-level boundary
    │         Isolates feature failure from rest of page
    │
    └── NO → Is this a single component that might fail?
        ├── YES → Component-level boundary
        │         Granular recovery, minimal UI disruption
        │
        └── NO → No boundary needed here

Placement Levels

// Level 1: Page-level (root safety net) — ALWAYS have this
<ErrorBoundary fallback={<FullPageError />}>
  <App />
</ErrorBoundary>

// Level 2: Feature-level (isolate independent sections)
<ErrorBoundary fallback={<SidebarError />}>
  <Sidebar />
</ErrorBoundary>
<ErrorBoundary fallback={<FeedError />}>
  <NewsFeed />
</ErrorBoundary>

// Level 3: Component-level (granular recovery)
<ErrorBoundary fallback={<ChartPlaceholder />}>
  <RevenueChart data={data} />
</ErrorBoundary>

ALWAYS have at minimum a page-level error boundary wrapping the entire application.

NEVER wrap every single component in a boundary — this creates excessive overhead and fragments the UI. Use boundaries at natural isolation points.

---

Error Recovery Patterns

Pattern 1: Key Prop Reset

Force React to unmount and remount the component by changing the key:

function RecoverableWidget({ userId }: { userId: string }) {
  const [errorKey, setErrorKey] = useState<number>(0);

  return (
    <ErrorBoundary
      key={errorKey}
      fallback={
        <button onClick={() => setErrorKey((k) => k + 1)}>
          Retry
        </button>
      }
    >
      <UserWidget userId={userId} />
    </ErrorBoundary>
  );
}

Pattern 2: Reset Callback

Use the boundary's internal reset method (as shown in the complete implementation above):

<ErrorBoundary
  fallback={(error, reset) => (
    <div role="alert">
      <p>Failed to load: {error.message}</p>
      <button onClick={reset}>Try Again</button>
    </div>
  )}
>
  <DataTable />
</ErrorBoundary>

Pattern 3: Navigate Away

Redirect to a safe route on error:

function NavigatingFallback({ error }: { error: Error }) {
  return (
    <div role="alert">
      <p>This page encountered an error.</p>
      <a href="/">Return to Home</a>
    </div>
  );
}

---

Nested Boundaries

Inner boundaries catch errors first. If the inner boundary itself fails, the outer boundary catches it.

<ErrorBoundary fallback={<AppCrashScreen />}>       {/* Outer: last resort */}
  <Header />
  <ErrorBoundary fallback={<ContentError />}>        {/* Inner: feature-level */}
    <ErrorBoundary fallback={<WidgetError />}>       {/* Innermost: component */}
      <ComplexWidget />
    </ErrorBoundary>
    <OtherContent />
  </ErrorBoundary>
  <Footer />
</ErrorBoundary>

Behavior: If ComplexWidget throws, the innermost boundary shows <WidgetError />. The rest of the page (Header, OtherContent, Footer) remains functional.

---

react-error-boundary Library

The react-error-boundary package provides a function-component-based API. ALWAYS use this library instead of writing custom class components.

npm install react-error-boundary

ErrorBoundary Component

import { ErrorBoundary } from 'react-error-boundary';

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

<ErrorBoundary
  FallbackComponent={ErrorFallback}
  onError={(error, info) => logToService(error, info)}
  onReset={(details) => {
    // Reset app state that caused the error
  }}
  resetKeys={[userId]}  // Auto-reset when these values change
>
  <UserProfile userId={userId} />
</ErrorBoundary>

useErrorBoundary Hook

Trigger the nearest error boundary from event handlers or async code:

import { useErrorBoundary } from 'react-error-boundary';

function UserActions() {
  const { showBoundary } = useErrorBoundary();

  async function handleDelete() {
    try {
      await deleteUser();
    } catch (error) {
      showBoundary(error);  // Triggers nearest ErrorBoundary
    }
  }

  return <button onClick={handleDelete}>Delete Account</button>;
}

This solves the limitation that error boundaries cannot catch event handler or async errors.

---

React 19: Root-Level Error Callbacks

React 19 adds error reporting callbacks on createRoot:

import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root')!, {
  onCaughtError: (error, errorInfo) => {
    // Errors caught by an Error Boundary
    console.error('Caught by boundary:', error, errorInfo.componentStack);
  },
  onUncaughtError: (error, errorInfo) => {
    // Errors NOT caught by any Error Boundary
    console.error('Uncaught:', error, errorInfo.componentStack);
  },
  onRecoverableError: (error, errorInfo) => {
    // Errors React recovers from automatically
    console.error('Recovered:', error, errorInfo.componentStack);
  },
});

These callbacks complement error boundaries — they do NOT replace them.

---

Testing Error Boundaries

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

// Component that throws on demand
function ThrowingComponent({ shouldThrow }: { shouldThrow: boolean }) {
  if (shouldThrow) {
    throw new Error('Test error');
  }
  return <p>Content is fine</p>;
}

describe('ErrorBoundary', () => {
  // Suppress console.error noise in test output
  const originalError = console.error;
  beforeAll(() => { console.error = vi.fn(); });
  afterAll(() => { console.error = originalError; });

  it('renders children when no error', () => {
    render(
      <ErrorBoundary fallback={<p>Error occurred</p>}>
        <ThrowingComponent shouldThrow={false} />
      </ErrorBoundary>
    );
    expect(screen.getByText('Content is fine')).toBeInTheDocument();
  });

  it('renders fallback when child throws', () => {
    render(
      <ErrorBoundary fallback={<p>Error occurred</p>}>
        <ThrowingComponent shouldThrow={true} />
      </ErrorBoundary>
    );
    expect(screen.getByText('Error occurred')).toBeInTheDocument();
  });

  it('calls onError callback with error details', () => {
    const onError = vi.fn();
    render(
      <ErrorBoundary fallback={<p>Error</p>} onError={onError}>
        <ThrowingComponent shouldThrow={true} />
      </ErrorBoundary>
    );
    expect(onError).toHaveBeenCalledWith(
      expect.objectContaining({ message: 'Test error' }),
      expect.objectContaining({ componentStack: expect.any(String) })
    );
  });
});

---

Development vs Production Behavior

BehaviorDevelopmentProduction
Error bubblingErrors bubble to window.onerror even when caughtOnly caught by error boundaries
Console outputReact logs full component stackMinimal logging
Strict ModeComponents render twice to detect impuritiesSingle render

---

Reference Links

  • references/examples.md -- Error boundary patterns and real-world implementations
  • references/anti-patterns.md -- Common error boundary mistakes and how to avoid them

Official Sources

  • https://react.dev/reference/react/Component (getDerivedStateFromError, componentDidCatch)
  • https://react.dev/reference/react-dom/client/createRoot (onCaughtError, onUncaughtError)
  • https://github.com/bvaughn/react-error-boundary (react-error-boundary library)

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.