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 wrapperFeedbackDialog- 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