← Back to React Course | Chapter 13: Advanced React & Architecture | Lesson 1 of 14

Error Boundaries

An error boundary is like a circuit breaker in your house — if one appliance short-circuits, it trips just that circuit instead of plunging the whole house into darkness.

What Error Boundaries Catch

Without an error boundary, a JavaScript error thrown anywhere during rendering unmounts the ENTIRE React component tree, showing a blank page. An error boundary catches errors thrown by its child components during rendering, lifecycle methods, and constructors, letting you show a fallback UI instead of a blank screen.

Note: Wrap risky or complex sections of your UI (like a widget rendering untrusted third-party data) in their own error boundary.

Warning: Error boundaries do NOT catch errors inside event handlers (like an onClick handler throwing) — those need regular try/catch instead.

Example: What Error Boundaries Catch

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError() { return { hasError: true }; }
  render() {
    if (this.state.hasError) return <p>Something went wrong.</p>;
    return this.props.children;
  }
}
function BuggyComponent() { throw new Error("Oops"); }
function App() {
  return <ErrorBoundary><BuggyComponent /></ErrorBoundary>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

Logging Errors with componentDidCatch

Alongside getDerivedStateFromError (which updates state to show the fallback UI), componentDidCatch is a second lifecycle method that receives the actual error and additional info, commonly used to log the error to a monitoring service without affecting what's rendered.

Note: Keep getDerivedStateFromError focused purely on updating state; use componentDidCatch for side effects like logging.

Warning: componentDidCatch alone (without getDerivedStateFromError) won't update the UI to a fallback — you need both methods working together for the typical pattern.

Example: Logging Errors with componentDidCatch

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError() { return { hasError: true }; }
  componentDidCatch(error, info) {
    console.log("Logged error:", error.message);
  }
  render() {
    if (this.state.hasError) return <p>Error logged and handled.</p>;
    return this.props.children;
  }
}
function BuggyComponent() { throw new Error("Simulated crash"); }
function App() {
  return <ErrorBoundary><BuggyComponent /></ErrorBoundary>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

Using Multiple, Scoped Error Boundaries

Instead of one giant error boundary around the whole app (which would blank out everything on any single error), wrapping smaller, independent sections in their own boundaries limits the impact — if a sidebar widget crashes, the rest of the page keeps working normally.

Note: Good boundary boundaries are natural feature boundaries: a comments section, a chart widget, a third-party embed — each independently recoverable.

Warning: A single app-wide error boundary means ANY error anywhere blanks the entire UI, defeating much of the benefit of using boundaries at all.

Example: Using Multiple, Scoped Error Boundaries

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError() { return { hasError: true }; }
  render() { return this.state.hasError ? <p>Widget failed to load.</p> : this.props.children; }
}
function BrokenWidget() { throw new Error("Widget crash"); }
function App() {
  return (
    <div>
      <p>This part still works fine.</p>
      <ErrorBoundary><BrokenWidget /></ErrorBoundary>
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>
Common Mistakes
  1. Trying to write an error boundary as a function component — as of React 18, error boundaries must be class components.
  2. Expecting an error boundary to catch errors in event handlers — it only catches errors during rendering, lifecycle methods, and constructors.
  3. Wrapping the entire app in just one error boundary, so any error takes down the whole UI instead of just the broken section.
Chapter Summary
  • An error boundary is a class component that catches JavaScript errors in its child component tree during rendering.
  • getDerivedStateFromError and componentDidCatch are the two lifecycle methods that implement this behavior.
  • Error boundaries do NOT catch errors in event handlers, async code, or the boundary's own rendering.
  • Wrapping smaller, independent sections of the UI in separate boundaries limits the blast radius of any one error.
Browser Support

Available since React 16.0 (error boundaries introduced); still require a class component as of React 18.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.