Skip to main content

ErrorFallback Component

ErrorFallback Component

Component: src/components/shared/ErrorFallback.tsx Access: Renders when ErrorBoundary catches an error

---

Overview

The ErrorFallback component displays a user-friendly error page when an unhandled JavaScript error occurs. It provides crash reporting functionality via the send-crash-report Edge Function and allows users to submit additional context.

Features

  • Error display - Shows error message in a friendly format
  • Crash reporting - Sends error details to support team
  • User feedback - Optional text field for user context
  • Console log capture - Includes recent console logs in report
  • Recovery options - Reload page or go to dashboard
  • Development mode - Shows full stack trace in dev

UI Layout

Full-Screen ErrorFallback (Root Level)

┌─────────────────────────────────────────────────────────┐
│                                                         │
│                    ⚠️                                   │
│           Oops! Something crashed                       │
│                                                         │
│  We're sorry for the inconvenience. Please send us a    │
│  crash report so we can fix this issue.                 │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  What were you doing when this happened? (optional)     │
│  ┌─────────────────────────────────────────────────────┐│
│  │                                                     ││
│  │                                                     ││
│  └─────────────────────────────────────────────────────┘│
│                                                         │
│  [Send Crash Report & Refresh]                          │
│  Skip & Refresh                                         │
│                                                         │
└─────────────────────────────────────────────────────────┘

PageErrorFallback (Hub/Module Level)

┌─────────────────────────────────────────────────────────┐
│                                                         │
│                    🔄                                   │
│           Whoops! We need a refresh                     │
│                                                         │
│  Don't worry, your data is safe. A quick refresh        │
│  should fix this.                                       │
│                                                         │
│              [Refresh Page]                             │
│         Or try again without refreshing                 │
│                                                         │
│  ▸ Technical details                                    │
│                                                         │
└─────────────────────────────────────────────────────────┘

Props

interface ErrorFallbackProps {
  error: Error;
  resetErrorBoundary: () => void;
  componentStack?: string;
}

State Management

const [userMessage, setUserMessage] = useState('');
const [isSending, setIsSending] = useState(false);
const [sent, setSent] = useState(false);

Crash Report Submission

const handleSendReport = async () => {
  setIsSending(true);
  
  try {
    await fetch(`${SUPABASE_URL}/functions/v1/send-crash-report`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        error: {
          message: error.message,
          stack: error.stack,
          name: error.name
        },
        componentStack,
        consoleLogs: getRecentConsoleLogs(),
        userMessage,
        metadata: {
          url: window.location.href,
          userAgent: navigator.userAgent,
          timestamp: new Date().toISOString(),
          userId: getCurrentUserId(),
          organizationId: getCurrentOrganizationId()
        }
      })
    });
    
    setSent(true);
    toast.success('Report sent. Thank you for helping us improve!');
  } catch (err) {
    toast.error('Failed to send report. Please try again.');
  } finally {
    setIsSending(false);
  }
};

Console Log Capture

The component captures recent console logs to help with debugging:

const getRecentConsoleLogs = (): ConsoleLog[] => {
  // Returns last 50 console entries captured by global interceptor
  return window.__consoleLogs || [];
};

// Global console interceptor (set up in App.tsx)
const originalConsole = { ...console };
window.__consoleLogs = [];

['log', 'warn', 'error', 'info'].forEach(method => {
  console[method] = (...args) => {
    window.__consoleLogs.push({
      type: method,
      message: args.map(a => String(a)).join(' '),
      timestamp: new Date().toISOString()
    });
    
    // Keep only last 50 entries
    if (window.__consoleLogs.length > 50) {
      window.__consoleLogs.shift();
    }
    
    originalConsole[method] (...args);
  };
});

Recovery Actions

Full-Screen ErrorFallback

const handleRefresh = () => {
  window.location.reload();
};

PageErrorFallback (with cache clearing)

const handleRefresh = () => {
  // Clear caches before reload for stale cache errors
  if ('caches' in window) {
    caches.keys().then(names => {
      names.forEach(name => caches.delete(name));
    });
  }
  window.location.reload();
};

const handleTryAgain = () => {
  resetErrorBoundary();
};

Stale Cache Detection

The PageErrorFallback component detects likely stale cache/deployment errors and hides the "Try Again" option for those cases:

const isLikelyStaleCache = useMemo(() => {
  const message = error?.message || '';
  return (
    message.includes('Minified React error') ||
    message.includes('Failed to fetch dynamically imported module') ||
    message.includes('Loading chunk') ||
    message.includes('Loading CSS chunk') ||
    message.includes('is not defined') ||
    message.includes('is not a function') ||
    message.includes('Network Error')
  );
}, [error]);

Auto-refresh in-flight UI (stale chunks)

When PageErrorFallback schedules an automatic reload for likely stale deployment chunks (loop-guarded, ~1.5s delay), the in-flight state shows `DuckLoadingScreen` (src/components/shared/DuckLoadingScreen.tsx, variant="overlay") — mascot only (small spinning rubber duck from src/assets/alignmint_duck.svg via SpinningRubberDuck.tsx), no headings, body copy, or buttons (screen readers get status.loading via aria-label). This is the only in-app use of the duck; other loading uses `PlainLoadingScreen` / `Loader2`. See documentation/frontend/ERROR-HANDLING.md § Auto-Refresh for Stale Cache Errors. Styling: documentation/frontend/STYLING-GUIDE.md § DuckLoadingScreen.

Development Mode

In development, shows additional debugging info:

{process.env.NODE_ENV === 'development' && (
  <details className="mt-4 text-left">
    <summary className="cursor-pointer text-sm text-muted-foreground">
      Error Details (Development Only)
    </summary>
    <pre className="mt-2 p-4 bg-muted rounded text-xs overflow-auto">
      {error.stack}
    </pre>
    {componentStack && (
      <pre className="mt-2 p-4 bg-muted rounded text-xs overflow-auto">
        {componentStack}
      </pre>
    )}
  </details>
)}

Usage with ErrorBoundary

// App.tsx
import { ErrorBoundary } from 'react-error-boundary';
import { ErrorFallback } from './components/ErrorFallback';

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onReset={() => {
        // Reset app state if needed
      }}
    >
      <MainContent />
    </ErrorBoundary>
  );
}

Related Components

  • ErrorBoundary - React error boundary wrapper
  • FeedbackDialog - General feedback submission

Related Documentation

  • Edge Functions: ../../backend/EDGE-FUNCTIONS.md (send-crash-report)
  • Error Handling: ../../frontend/ERROR-HANDLING.md

Synced from IFMmvp-Frontend documentation: pages/components/22-ERROR-FALLBACK.md

Ready to Get Started?

See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.

Questions? Email us at steven@getalignmint.org

Ready to get started?Start Plus Trial