REACT Lesson 24 – Error Boundaries | Dataplexa
Lesson 24

Error Boundaries

Build safety nets that catch JavaScript errors before they crash your DataFlow dashboard.

Your DataFlow dashboard is humming along perfectly. Users are viewing charts, filtering data, clicking buttons. Then someone's internet drops during an API call. Or a prop comes back undefined from your backend. Suddenly the entire screen goes white with a cryptic error message. React components can throw errors just like regular JavaScript functions. But unlike a broken function that only affects one calculation, a broken React component can crash your entire app. Every component above it in the tree stops rendering. Your users see nothing. Error boundaries are React's answer to this problem. Think of them as try-catch blocks for your component tree. They catch JavaScript errors anywhere in their child components and display a fallback UI instead of the white screen of death.

What Are Error Boundaries

An error boundary is a React component that catches JavaScript errors during rendering, in lifecycle methods, and in constructors of the whole tree below them. They're like safety nets for your UI. Error boundaries work by implementing special lifecycle methods that React calls when an error occurs. But here's the catch - error boundaries must be class components. React hooks don't have equivalents for these error-catching methods yet. Error boundaries catch errors in three places: during rendering when React builds the virtual DOM, in lifecycle methods like useEffect, and in constructors when components initialize. They don't catch errors in event handlers, asynchronous code like setTimeout, or errors thrown in the error boundary itself.

Class Components Required

Error boundaries must be class components because React hooks don't provide error-catching capabilities. Even in modern React codebases using functional components everywhere, you'll need at least one class component for error handling.

Creating an Error Boundary

Creating an error boundary requires implementing two lifecycle methods in a class component. The static getDerivedStateFromError method catches the error and updates state, while componentDidCatch handles logging and side effects.
// DataFlow error boundary to protect dashboard sections
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    // Update state to show fallback UI
    return { hasError: true, error: error };
  }

  componentDidCatch(error, errorInfo) {
    // Log error for debugging
    console.error('DataFlow Error:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div style={{padding: '20px', textAlign: 'center'}}>
          <h2>Something went wrong</h2>
          <p>The DataFlow dashboard encountered an error.</p>
        </div>
      );
    }
    return this.props.children;
  }
}
DataFlow Error Boundary

What just happened?

The error boundary wrapped its children but didn't activate because no errors occurred. The getDerivedStateFromError method only runs when JavaScript errors are thrown during rendering.

The getDerivedStateFromError method receives the error as a parameter and returns an object to update the component's state. This method runs during the render phase, so it can't perform side effects like logging. That's where componentDidCatch comes in. The componentDidCatch method receives both the error and an error info object containing details about which component threw the error. This method runs during the commit phase, making it perfect for logging errors to external services or analytics platforms.

Triggering Error Boundaries

Error boundaries only activate when JavaScript errors occur during rendering. Event handler errors don't trigger error boundaries because they happen outside the React rendering cycle. You need to handle those separately with regular try-catch blocks.
// Component that throws an error during rendering
function ProblematicChart({ data }) {
  // This will crash if data is null or undefined
  const revenue = data.revenue.toFixed(2);
  
  return (
    <div>
      <h3>Revenue: ${revenue}</h3>
    </div>
  );
}

// Using it with error boundary protection
function Dashboard() {
  return (
    <ErrorBoundary>
      <ProblematicChart data={null} />
    </ErrorBoundary>
  );
}
DataFlow Error Demo

What just happened?

The ProblematicChart tried to call toFixed on null, which threw a TypeError during rendering. The error boundary caught this error and displayed the fallback UI instead of crashing the entire app.

Common scenarios that trigger error boundaries include accessing properties on undefined objects, calling methods on null values, and array operations on non-arrays. API calls that return unexpected data structures are frequent culprits in real applications.

Strategic Error Boundary Placement

Where you place error boundaries determines how much of your app stays functional when errors occur. Place them too high and small errors take down large sections. Place them too low and you end up with boundary components everywhere. The DataFlow team uses a layered approach. They have a top-level error boundary that catches catastrophic errors and displays a "something went wrong" message. Then they place more specific boundaries around risky components like charts, data tables, and API-dependent sections.
// Strategic error boundary placement in DataFlow
function Dashboard() {
  return (
    <ErrorBoundary> {/* Top-level protection */}
      <Header />
      <div className="dashboard-content">
        <Sidebar />
        <main>
          <ChartErrorBoundary> {/* Chart-specific boundary */}
            <RevenueChart />
            <GrowthChart />
          </ChartErrorBoundary>
          
          <TableErrorBoundary> {/* Table-specific boundary */}
            <TransactionTable />
          </TableErrorBoundary>
        </main>
      </div>
    </ErrorBoundary>
  );
}
DataFlow Layered Protection

What just happened?

The chart section threw an error, but only the chart error boundary activated. The table section and header remained fully functional. This demonstrates how proper error boundary placement isolates failures to specific features rather than crashing the entire dashboard.

This layered approach keeps your app functional even when individual features fail. Netflix uses similar strategies - when a movie thumbnail fails to load, it doesn't break the entire browse page. Only that specific thumbnail shows an error state.

Error Boundary Limitations

Error boundaries have important limitations. They don't catch errors in event handlers, asynchronous code like promises or setTimeout, server-side rendering, or errors thrown in the error boundary itself. Event handler errors need regular try-catch blocks because they execute outside React's rendering cycle. The same applies to async operations - wrap API calls in try-catch blocks or use error states in your components.

Error Boundaries Won't Catch These

Event handler errors, Promise rejections, setTimeout/setInterval errors, server-side rendering errors, and errors in the error boundary component itself all bypass error boundaries. Handle these scenarios with traditional try-catch blocks and error states.

// Error boundary won't catch this event handler error
function DataControls() {
  const handleExport = () => {
    try {
      // This error won't trigger error boundaries
      const data = null;
      data.forEach(item => console.log(item));
    } catch (error) {
      console.error('Export failed:', error);
      // Show user-friendly error message
    }
  };

  return (
    <button onClick={handleExport}>
      Export Data
    </button>
  );
}
DataFlow Event Handler

Production Error Handling

In production, error boundaries become crucial for user experience and debugging. You want to show users helpful error messages while logging detailed error information for your development team. The DataFlow team integrates their error boundaries with services like Sentry or LogRocket for comprehensive error tracking. Error boundaries in production should provide recovery options when possible. Reset buttons, refresh suggestions, or fallback content help users continue their work even when errors occur. Notion's interface handles component failures gracefully by showing inline error states that don't disrupt the entire page.

Development Mode

Show detailed error messages, stack traces, and component trees. Help developers debug issues quickly.

Production Mode

Display user-friendly messages, provide recovery actions, and log errors to monitoring services.

The key is balancing user experience with debugging information. Users don't need to see stack traces, but your error monitoring service does. Create error boundaries that adapt their behavior based on environment settings. Error boundaries work alongside other error handling strategies. Use them for component-level failures, implement global error handlers for unhandled Promise rejections, and add try-catch blocks in event handlers. This comprehensive approach catches errors at every level of your application. React's error boundary system provides the safety net your applications need. By implementing them strategically throughout your component tree, you can isolate failures and keep your DataFlow dashboard running smoothly even when individual features encounter problems.

Quiz

1. The DataFlow team needs to create an error boundary. Which two lifecycle methods must they implement in their class component?


2. A DataFlow button click handler throws an error when processing export data. What type of error will an error boundary NOT catch?


3. The DataFlow dashboard has charts, tables, and a sidebar. What's the best strategy for error boundary placement?


React Best Practices

Master the essential patterns and conventions that separate professional React code from beginner implementations.