import React, { Component, ReactNode } from 'react'
interface Props {
children: ReactNode
fallback?: ReactNode
onError?: (error: Error, errorInfo: React.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: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo)
// Log to error tracking service
this.props.onError?.(error, errorInfo)
}
render() {
if (this.state.hasError) {
return (
this.props.fallback || (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full bg-white shadow-lg rounded-lg p-6">
<div className="flex items-center gap-3 mb-4">
<i className="fas fa-exclamation-triangle text-red-500 text-2xl"></i>
<h2 className="text-xl font-bold">Something went wrong</h2>
</div>
<p className="text-gray-600 mb-4">
We're sorry for the inconvenience. Please try refreshing the page.
</p>
<button
onClick={() => window.location.reload()}
className="w-full px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Refresh Page
</button>
</div>
</div>
)
)
}
return this.props.children
}
}
function App() {
return (
<ErrorBoundary onError={(error, errorInfo) => {
// Send to error tracking service
// logErrorToService(error, errorInfo)
}}>
<Router>
{/* App content */}
</Router>
</ErrorBoundary>
)
}
React error boundaries catch JavaScript errors in child components, log them, and display fallback UI instead of crashing the entire app. Since error boundaries must be class components in React, I create a generic ErrorBoundary wrapper and use it around route components or major features. The componentDidCatch lifecycle method receives the error and component stack trace for logging to error tracking services like Sentry. Error boundaries don't catch errors in event handlers, async code, or SSR—those need try/catch. I render different fallback UIs based on the error type: full-page errors for route-level failures, inline errors for widget failures. This defensive programming prevents one broken component from ruining the entire user experience.
Related snips
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
const success = true;
setTimeout(() => {
if (success) {
Promises and async/await patterns for asynchronous JavaScript
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
timeout: 15000,
Axios API client with interceptors
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface FilterState {
search: string
category: string | null
Zustand for lightweight state management
import React from "react";
type FallbackProps = {
error: Error;
reset: () => void;
};
React Error Boundary + error reporting hook
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
openAnalyzer: true,
});
/** @type {import('next').NextConfig} */
Next.js bundle analyzer for targeted performance work
Share this code
Here's the card — post it anywhere.