Skip to content
Development
Skill

/error-boundary

React error boundary hierarchy, fallback UI patterns, offline-first fallback, retry mechanisms, and graceful degradation.

From plugin
vibecosystem
531200 skills138 agents7 hooks
Install
$ npx -y skills add vibeeval/vibecosystem --skill error-boundary --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/error-boundary

Context preview

The summary Claude sees to decide when to auto-load this skill.

React error boundary hierarchy, fallback UI patterns, offline-first fallback, retry mechanisms, and graceful degradation.

SKILL.md

error-boundary.SKILL.md
name: error-boundary
description: React error boundary hierarchy, fallback UI patterns, offline-first fallback, retry mechanisms, and graceful degradation.

Error Boundary Patterns

React error boundary hierarchy and graceful degradation patterns.

Error Boundary Component

// Class-based (React requirement for componentDidCatch)
interface ErrorBoundaryProps {
  children: React.ReactNode
  fallback?: React.ComponentType<FallbackProps>
  onError?: (error: Error, info: React.ErrorInfo) => void
}

interface ErrorBoundaryState {
  error: Error | null
}

export interface FallbackProps {
  error: Error
  reset: () => void
}

export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
  state: ErrorBoundaryState = { error: null }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { error }
  }

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    console.error('[ErrorBoundary]', error, info.componentStack)
    this.props.onError?.(error, info)
  }

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

  render() {
    if (this.state.error) {
      const Fallback = this.props.fallback ?? DefaultFallback
      return <Fallback error={this.state.error} reset={this.reset} />
    }
    return this.props.children
  }
}

// react-error-boundary library (recommended — battle-tested)
// npm install react-error-boundary
import { ErrorBoundary } from 'react-error-boundary'

<ErrorBoundary
  FallbackComponent={MyFallback}
  onError={(error, info) => reportToSentry(error, info)}
  onReset={() => queryClient.clear()}
>
  <App />
</ErrorBoundary>

Error Boundary Hierarchy

App (top-level — catches everything, shows full-page error)
├── Navigation (no boundary — nav errors bubble to app)
├── <ErrorBoundary fallback={PageError}>           ← page level
│   └── DashboardPage
│       ├── <ErrorBoundary fallback={SectionError}>  ← section level
│       │   └── StatsSection
│       └── <ErrorBoundary fallback={WidgetError}>   ← widget level
│           └── ChartWidget                           (one fails, others work)
└── Sidebar (separate boundary — sidebar error ≠ main content error)
// Page-level boundary (reset on navigation)
import { usePathname } from 'next/navigation'
import { ErrorBoundary } from 'react-error-boundary'

export function PageErrorBoundary({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()
  return (
    <ErrorBoundary
      key={pathname}              // reset boundary on route change
      FallbackComponent={PageErrorFallback}
      onError={reportError}
    >
      {children}
    </ErrorBoundary>
  )
}

// Widget-level boundary (isolated, inline fallback)
export function WidgetErrorBoundary({ children, name }: { children: React.ReactNode; name: string }) {
  return (
    <ErrorBoundary
      fallback={<WidgetErrorFallback name={name} />}
      onError={(err) => reportError(err, { widget: name })}
    >
      {children}
    </ErrorBoundary>
  )
}

Fallback UI Design Patterns

// Full-page fallback (app boundary)
function PageErrorFallback({ error, reset }: FallbackProps) {
  return (
    <div className="flex min-h-screen flex-col items-center justify-center gap-4 p-8 text-center">
      <h1 className="text-2xl font-bold text-gray-900">Something went wrong</h1>
      <p className="max-w-md text-gray-500">
        {process.env.NODE_ENV === 'development'
          ? error.message
          : 'An unexpected error occurred. Our team has been notified.'}
      </p>
      <div className="flex gap-3">
        <button onClick={reset} className="btn btn-primary">Try again</button>
        <a href="/" className="btn btn-outline">Go home</a>
      </div>
      {process.env.NODE_ENV === 'development' && (
        <pre className="mt-4 max-w-2xl overflow-auto rounded bg-red-50 p-4 text-left text-sm text-red-800">
          {error.stack}
        </pre>
      )}
    </div>
  )
}

// Inline section fallback (section boundary)
function SectionErrorFallback({ error, reset }: FallbackProps) {
  return (
    <div className="rounded-lg border border-red-200 bg-red-50 p-6 text-center">
      <p className="text-red-700 font-medium">Failed to load this section</p>
      <button onClick={reset} className="mt-3 text-sm text-red-600 underline hover:no-underline">
        Retry
      </button>
    </div>
  )
}

// Toast-style fallback (non-critical widget)
function WidgetErrorFallback({ name }: { name: string }) {
  return (
    <div className="flex items-center gap-2 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-700">
      <span>⚠</span>
      <span>{name} unavailable</span>
    </div>
  )
}

Retry Mechanism in Error Boundaries

import { ErrorBoundary } from 'react-error-boundary'
import { useState, useCallback } from 'react'

function RetryFallback({ error, reset }: FallbackProps) {
  const [retries, setRetries] = useState(0)
  const maxRetries = 3

  const handleRetry = useCallback(() => {
    if (retries < maxRetries) {
      setRetries(r => r + 1)
      reset()
    }
  }, [retries, reset])

  return (
    <div className="rounded-lg border bg-white p-6 text-center">
      <p className="font-medium text-gray-900">Failed to load</p>
      {retries < maxRetries ? (
        <button onClick={handleRetry} className="mt-4 btn btn-sm">
          Retry ({maxRetries - retries} left)
        </button>
      ) : (
        <p className="mt-4 text-sm text-gray-400">
          Please refresh the page or <a href="/support" className="underline">contact support</a>.
        </p>
      )}
    </div>
  )
}

Error Reporting (Sentry Integration)

import * as Sentry from '@sentry/nextjs'

export function reportError(
  error: Error,
  context?: Record<string, unknown>,
  info?: React.ErrorInfo
) {
  if (process.env.NODE_ENV === 'development') {
    console.error('[Error]', error, context)
    return
Read more
Ships withvibecosystem

Your AI software team. Built on Claude Code. vibecosystem turns Claude Code into a full AI software team — 138 specialized agents that plan, build, review, test, and learn from every mistake. No configuration needed — just install and code.

Get the whole plugin

Other skills on vibecosystem.