Skip to main content

Overview

The ErrorBoundary component is a React error boundary that catches JavaScript errors anywhere in the component tree, logs them, and displays a fallback UI instead of crashing the entire application.

Usage

import { ErrorBoundary } from '@/components'

function App() {
  return (
    <ErrorBoundary>
      <YourApp />
    </ErrorBoundary>
  )
}

Props

children
ReactNode
required
The component tree to protect with the error boundary
fallback
ReactNode
Optional custom fallback UI to display when an error occurs. If not provided, the default ErrorPage component is used.

Features

Error Catching

The error boundary catches errors during:
  • Rendering
  • Lifecycle methods
  • Constructors of child components
static getDerivedStateFromError(error) {
  return { hasError: true, error }
}

Error Logging

Errors are automatically logged to the console:
componentDidCatch(error, errorInfo) {
  console.error("Uncaught error:", error, errorInfo)
}

Error Recovery

The component provides a resetErrorBoundary method to attempt recovery:
resetErrorBoundary = () => {
  this.setState({ hasError: false, error: null })
}

Default Error UI

If no custom fallback is provided, the component displays an ErrorPage with:
  • Error icon
  • User-friendly message in Spanish
  • Error details (in development)
  • “Try again” button
  • “Return home” link

Component Structure

import { Component } from 'react'
import { ErrorPage } from './ErrorPage'

export class ErrorBoundary extends Component {
  constructor(props) {
    super(props)
    this.state = { hasError: false, error: null }
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error }
  }

  componentDidCatch(error, errorInfo) {
    console.error("Uncaught error:", error, errorInfo)
  }

  resetErrorBoundary = () => {
    this.setState({ hasError: false, error: null })
  }

  render() {
    if (this.state.hasError) {
      if (this.props.fallback) {
        return this.props.fallback
      }
      return <ErrorPage error={this.state.error} resetErrorBoundary={this.resetErrorBoundary} />
    }

    return this.props.children
  }
}

Usage Patterns

App-Level Boundary

Wrap your entire application:
import { ErrorBoundary } from '@/components'

function App() {
  return (
    <ErrorBoundary>
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/jobs" element={<Jobs />} />
        </Routes>
      </Router>
    </ErrorBoundary>
  )
}

Route-Level Boundaries

Wrap individual routes for isolated error handling:
import { ErrorBoundary } from '@/components'

function Routes() {
  return (
    <>
      <Route path="/" element={<Home />} />
      <Route 
        path="/jobs" 
        element={
          <ErrorBoundary>
            <Jobs />
          </ErrorBoundary>
        } 
      />
      <Route 
        path="/jobs/:id" 
        element={
          <ErrorBoundary>
            <JobDetail />
          </ErrorBoundary>
        } 
      />
    </>
  )
}

Component-Level Boundaries

Wrap specific components:
import { ErrorBoundary } from '@/components'

function Dashboard() {
  return (
    <div>
      <Header />
      
      <ErrorBoundary>
        <JobListings jobs={jobs} />
      </ErrorBoundary>
      
      <ErrorBoundary>
        <UserProfile />
      </ErrorBoundary>
      
      <Footer />
    </div>
  )
}

Custom Fallback UI

import { ErrorBoundary } from '@/components'

function CustomErrorFallback() {
  return (
    <div className="error-container">
      <h2>Something went wrong</h2>
      <p>Please refresh the page or contact support.</p>
    </div>
  )
}

function App() {
  return (
    <ErrorBoundary fallback={<CustomErrorFallback />}>
      <YourApp />
    </ErrorBoundary>
  )
}

With Error Reporting Service

import { Component } from 'react'
import * as Sentry from '@sentry/react'

export class ErrorBoundary extends Component {
  // ... existing code ...

  componentDidCatch(error, errorInfo) {
    // Log to console
    console.error("Uncaught error:", error, errorInfo)
    
    // Report to error tracking service
    Sentry.captureException(error, {
      contexts: {
        react: {
          componentStack: errorInfo.componentStack
        }
      }
    })
  }
  
  // ... rest of component ...
}

Error Recovery Strategies

Automatic Retry

import { ErrorBoundary } from '@/components'
import { useEffect, useState } from 'react'

function AutoRetryBoundary({ children }) {
  const [retryCount, setRetryCount] = useState(0)
  
  useEffect(() => {
    if (retryCount > 0 && retryCount < 3) {
      const timer = setTimeout(() => {
        window.location.reload()
      }, 2000)
      return () => clearTimeout(timer)
    }
  }, [retryCount])
  
  return (
    <ErrorBoundary 
      onError={() => setRetryCount(c => c + 1)}
    >
      {children}
    </ErrorBoundary>
  )
}

Reset on Route Change

import { ErrorBoundary } from '@/components'
import { useLocation } from 'react-router-dom'
import { useEffect, useRef } from 'react'

function RouterErrorBoundary({ children }) {
  const location = useLocation()
  const boundaryRef = useRef()
  
  useEffect(() => {
    // Reset error boundary when route changes
    if (boundaryRef.current?.resetErrorBoundary) {
      boundaryRef.current.resetErrorBoundary()
    }
  }, [location.pathname])
  
  return (
    <ErrorBoundary ref={boundaryRef}>
      {children}
    </ErrorBoundary>
  )
}

What Error Boundaries Don’t Catch

Error boundaries do NOT catch errors in:
  1. Event handlers - Use try-catch instead
  2. Asynchronous code - Use .catch() or try-catch in async functions
  3. Server-side rendering
  4. Errors in the error boundary itself

Handling Event Handler Errors

function MyComponent() {
  const [error, setError] = useState(null)
  
  const handleClick = () => {
    try {
      // Code that might throw
      riskyOperation()
    } catch (error) {
      setError(error)
      console.error('Event handler error:', error)
    }
  }
  
  if (error) {
    return <div>Error: {error.message}</div>
  }
  
  return <button onClick={handleClick}>Click me</button>
}

Handling Async Errors

function AsyncComponent() {
  const [error, setError] = useState(null)
  
  useEffect(() => {
    fetchData()
      .catch(error => {
        setError(error)
        console.error('Async error:', error)
      })
  }, [])
  
  if (error) {
    return <div>Error: {error.message}</div>
  }
  
  return <div>Content</div>
}

Styling

The ErrorPage component uses CSS modules (ErrorPage.module.css):
  • styles.container - Full-page error container
  • styles.content - Centered content wrapper
  • styles.icon - Error icon styling
  • styles.title - Error title
  • styles.message - Error message
  • styles.errorDetails - Technical error details
  • styles.actions - Action buttons container
  • styles.retryButton - Retry button
  • styles.homeButton - Home link button

Source Code

Locations:
  • ErrorBoundary: src/components/ErrorBoundary/ErrorBoundary.jsx:4
  • ErrorPage: src/components/ErrorBoundary/ErrorPage.jsx:4

Components Overview

View all available components

Loading

Loading states for better UX

Best Practices

  1. Use multiple boundaries - Don’t wrap everything in one boundary; isolate errors to specific sections
  2. Provide context - Show users what went wrong and what they can do
  3. Log errors - Always log to console and consider error tracking services
  4. Test error states - Intentionally throw errors during development to verify boundaries work
  5. Recovery options - Always provide a way for users to recover (retry, go home, etc.)
  6. Production vs Development - Show detailed errors in development, friendly messages in production

Testing

Trigger Errors for Testing

function BuggyComponent() {
  const [throwError, setThrowError] = useState(false)
  
  if (throwError) {
    throw new Error('Test error for error boundary')
  }
  
  return (
    <button onClick={() => setThrowError(true)}>
      Trigger Error
    </button>
  )
}

// Usage
<ErrorBoundary>
  <BuggyComponent />
</ErrorBoundary>

Accessibility

  • Error messages are clear and in plain language
  • Action buttons are keyboard accessible
  • Semantic HTML for better screen reader support
  • Links and buttons have descriptive text

Build docs developers (and LLMs) love