Error Boundaries
What are Error Boundaries?
Error boundaries are React components that catch JavaScript errors in their child component tree, log them, and display a fallback UI instead of crashing the entire app. They work like a try-catch block for React components.
import { Component } from 'react'
// Error boundaries MUST be class components (no hook equivalent yet)
class ErrorBoundary extends Component {
constructor(props) {
super(props)
this.state = { hasError: false, error: null, errorInfo: null }
}
// Called when a descendant throws — update state to show fallback
static getDerivedStateFromError(error) {
return { hasError: true, error }
}
// Called after error is caught — good for logging
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error)
console.error('Component stack:', errorInfo.componentStack)
// Log to error tracking service
// Sentry.captureException(error, { extra: errorInfo })
this.setState({ errorInfo })
}
render() {
if (this.state.hasError) {
// Custom fallback UI
if (this.props.fallback) {
return this.props.fallback
}
return (
<div className="error-boundary">
<h2>Something went wrong</h2>
<p>{this.state.error?.message}</p>
{process.env.NODE_ENV === 'development' && (
<details>
<summary>Error details</summary>
<pre>{this.state.errorInfo?.componentStack}</pre>
</details>
)}
<button onClick={() => this.setState({ hasError: false, error: null })}>
Try Again
</button>
</div>
)
}
return this.props.children
}
}
export default ErrorBoundary
// react-error-boundary — modern library (recommended)
// npm install react-error-boundary
import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary'
// Fallback component
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert" className="error-fallback">
<h2>Something went wrong</h2>
<p>{error.message}</p>
<button onClick={resetErrorBoundary}>Try Again</button>
</div>
)
}
// Usage with FallbackComponent
function App() {
return (
<ErrorBoundary
FallbackComponent={ErrorFallback}
onError={(error, info) => console.error(error, info)}
onReset={() => console.log('Reset!')}
>
<UserProfile />
</ErrorBoundary>
)
}
// Inline fallback
function App2() {
return (
<ErrorBoundary
fallback={<p>Something went wrong</p>}
>
<UserProfile />
</ErrorBoundary>
)
}
// useErrorBoundary hook — throw errors from event handlers
function BuggyButton() {
const { showBoundary } = useErrorBoundary()
async function handleClick() {
try {
await fetchData()
} catch (error) {
showBoundary(error) // propagate to nearest error boundary
}
}
return <button onClick={handleClick}>Fetch Data</button>
}
import ErrorBoundary from './ErrorBoundary'
// Wrap individual sections — granular error handling
function Dashboard() {
return (
<div className="dashboard">
{/* Each section has its own error boundary */}
<ErrorBoundary fallback={<p>Chart failed to load</p>}>
<RevenueChart />
</ErrorBoundary>
<ErrorBoundary fallback={<p>User list unavailable</p>}>
<UserList />
</ErrorBoundary>
<ErrorBoundary fallback={<p>Activity feed unavailable</p>}>
<ActivityFeed />
</ErrorBoundary>
</div>
)
}
// Component that throws — for testing
function BuggyComponent() {
throw new Error('I crashed!')
return <p>Never renders</p>
}
// Reset key — remount component when key changes
function App() {
const [resetKey, setResetKey] = React.useState(0)
return (
<ErrorBoundary
key={resetKey} // changing key remounts the boundary
FallbackComponent={({ resetErrorBoundary }) => (
<div>
<p>Error occurred</p>
<button onClick={() => setResetKey(k => k + 1)}>
Reload Component
</button>
</div>
)}
>
<BuggyComponent />
</ErrorBoundary>
)
}
Ready to Level Up Your Skills?
Explore 500+ free tutorials across 20+ languages and frameworks.