Production-Ready Error Boundaries in React: Patterns for Graceful Failures
Unhandled render errors crash your entire React tree. Error boundaries contain the damage.
Here's how to use them effectively in production.
The Problem
// If UserCard throws, the entire page goes blank
function Dashboard() {
return (
<div>
<Header />
<UserCard /> {/* if this throws, everything dies */}
<Analytics />
</div>
)
}
Basic Error Boundary
Error boundaries must be class components (for now):
import React, { Component, ReactNode } from 'react'
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error: Error | null
}
class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false, error: null }
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, info: React.ErrorInfo) {
console.error('ErrorBoundary caught:', error, info.componentStack)
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? <div>Something went wrong</div>
}
return this.props.children
}
}
Using react-error-boundary
Don't write your own — use the battle-tested library:
npm install react-error-boundary
import { ErrorBoundary } from 'react-error-boundary'
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div role="alert">
<h2>Something went wrong</h2>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
)
}
function Dashboard() {
return (
<div>
<Header />
<ErrorBoundary FallbackComponent={ErrorFallback}>
<UserCard />
</ErrorBoundary>
<Analytics />
</div>
)
}
Granular Boundaries
Wrap independent sections — a failure in one shouldn't affect others:
function Dashboard() {
return (
<div className="grid grid-cols-3">
<ErrorBoundary FallbackComponent={WidgetError}>
<RevenueWidget />
</ErrorBoundary>
<ErrorBoundary FallbackComponent={WidgetError}>
<UsersWidget />
</ErrorBoundary>
<ErrorBoundary FallbackComponent={WidgetError}>
<ActivityFeed />
</ErrorBoundary>
</div>
)
}
function WidgetError({ error, resetErrorBoundary }: FallbackProps) {
return (
<div className="border border-red-200 rounded p-4">
<p className="text-red-600 text-sm">Failed to load widget</p>
<button onClick={resetErrorBoundary} className="text-sm text-gray-500 mt-2">
Retry
</button>
</div>
)
}
Logging to Your Error Monitoring Service
import * as Sentry from '@sentry/nextjs'
function onError(error: Error, info: { componentStack: string }) {
Sentry.captureException(error, {
extra: { componentStack: info.componentStack },
})
}
<ErrorBoundary
FallbackComponent={ErrorFallback}
onError={onError}
>
<Dashboard />
</ErrorBoundary>
Reset on Route Change
import { useLocation } from 'react-router-dom'
import { ErrorBoundary } from 'react-error-boundary'
function App() {
const location = useLocation()
return (
<ErrorBoundary
FallbackComponent={ErrorFallback}
resetKeys={[location.pathname]} // auto-reset on navigation
>
<Routes />
</ErrorBoundary>
)
}
What Error Boundaries DON'T Catch
- Errors in event handlers (use try/catch)
- Errors in async code (use try/catch + state)
- Errors during SSR
- Errors thrown in the boundary itself
// This WON'T be caught by error boundary:
function Button() {
const handleClick = async () => {
try {
await submitForm() // async error — handle here
} catch (err) {
setError(err.message)
}
}
return <button onClick={handleClick}>Submit</button>
}
Route-Level Boundaries in Next.js
In Next.js App Router, use error.tsx:
// app/dashboard/error.tsx
'use client'
export default function DashboardError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>Dashboard failed to load</h2>
<button onClick={reset}>Try again</button>
</div>
)
}
This is a built-in error boundary for the entire /dashboard route segment.
Production Checklist
- [ ] Route-level
error.tsxfiles in Next.js - [ ] Granular boundaries around independent widgets
- [ ] Error reporting wired up (Sentry / LogRocket)
- [ ] Meaningful fallback UIs (not just white screens)
- [ ] Reset buttons so users can recover
- [ ] Async error handling in event handlers
The AI SaaS Starter Kit ships with Sentry integrated, route-level error boundaries configured, and all the production error handling patterns pre-wired. $99 one-time — clone and ship.
Top comments (0)