Cover image for blog post: "How to Use Error Boundaries in React"
โ† Back to blog posts

How to Use Error Boundaries in React

Stop a single rendering error from blanking your whole React app: build an Error Boundary from scratch and show a friendly fallback UI instead.

Published onOctober 09, 20226 minutes read

Table of Contents

One bad value in one component, and suddenly your users are staring at a completely blank page. No message, no way back, nothing. Error Boundaries are React's answer to that problem, and in this post I'll show you how to build one from scratch.

What are Error Boundaries?

Error Boundaries were introduced in React v16 as a way to catch tricky errors that occur during the render phase. Without one, an error thrown while rendering takes down the whole component tree: the app unmounts completely and the user just sees a blank web page, which is not ideal!

An Error Boundary is a component that wraps part of your tree. If anything inside it throws while rendering, the boundary catches the error and renders a fallback UI instead of letting the whole app crash.

You can learn more about React Error Boundaries via code snippets and interactive demos.

Let's get started

In this article, we'll build a React application from scratch and walk through Error Boundaries step by step.

Prerequisites

These are the versions I used when I wrote this:

Create a React application

Let's create a simple React application. Open a terminal in your project directory and run:

npx create-react-app client

Note (2025): Create React App has since been deprecated by the React team (announcement). For a new project today, a framework or a build tool such as Vite is a better starting point. Everything about Error Boundaries below works the same way.

Now let's open the project in Visual Studio Code:

cd client
code .

Cheers! Your folder structure should look something like this at this point:

Folder structure of the newly created React app open in Visual Studio Code

Encountering errors without Error Boundaries

Unexpected is expected. We'll inevitably run into unexpected errors in our applications while we develop, test and even in production. You might try to loop through an array that doesn't exist, or access a nested object property that isn't populated yet. Most of the time you have control over how your application behaves, but sometimes you don't.

Simulate the error

Let's create a file named BugDemo.js. The following component will help you understand what normally happens in a React application without any Error Boundary.

Code for the BugDemo.js counter component that simulates an error

If you'd like something to copy and paste, here's an equivalent version:

import { useState } from 'react';
 
function BugDemo() {
  const [counter, setCounter] = useState(0);
 
  if (counter === 5) {
    // Simulate a rendering error
    throw new Error('Oops! The counter crashed.');
  }
 
  return (
    <div>
      <h2>Counter: {counter}</h2>
      <button onClick={() => setCounter((c) => c + 1)}>+</button>
    </div>
  );
}
 
export default BugDemo;

If you import the component in App.js and run the application with npm start or yarn start, you'll see the screen below.

The BugDemo counter rendered in the browser with a plus button

When we keep pressing the plus button, the counter value increases. But what happens when the counter reaches 5?

Clicking the plus button until the counter reaches 5, at which point the whole page goes blank

Yes! A blank screen, and a shocked end user. Sometimes the whole application can crash like this, which is exactly why Error Boundaries are so important.

A retro computer screen showing a technical difficulties error GIF via GIPHY

Solve the problem with Error Boundaries

Create a new file in the component/ directory and paste in the code below.

Code for the ErrorBoundary component in the component directory

If you'd like to follow along in text, here's what a typical Error Boundary looks like:

import { Component } from 'react';
 
class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
 
  static getDerivedStateFromError() {
    // Update state so the next render shows the fallback UI
    return { hasError: true };
  }
 
  componentDidCatch(error, info) {
    // A good place to log the error to a reporting service
    console.error(error, info.componentStack);
  }
 
  render() {
    if (this.state.hasError) {
      return <h2>Something went wrong.</h2>;
    }
    return this.props.children;
  }
}
 
export default ErrorBoundary;

Two lifecycle methods do the work here:

Notice that this is a class component. According to the React docs, there's currently no way to write an Error Boundary as a function component. If you'd rather not write the class yourself, the react-error-boundary package does it for you.

Now, let's wrap our imported BugDemo component with the newly created Error Boundary in App.js, like this:

App.js wrapping the BugDemo component inside the ErrorBoundary component
import BugDemo from './component/BugDemo';
import ErrorBoundary from './component/ErrorBoundary';
 
function App() {
  return (
    <ErrorBoundary>
      <BugDemo />
    </ErrorBoundary>
  );
}
 
export default App;

The moment of truth

Let's check what happens now when the user reaches a counter value of 5. No more blank screens!

Clicking the plus button until the counter reaches 5, after which a fallback error message appears instead of a blank page

Likewise, you can show a helpful error message to the user, or retry the task that was failing before, if that's possible.

What Error Boundaries don't catch

Error Boundaries only catch errors thrown while rendering their children. As the React docs point out, they won't catch errors in:

Wrapping up

With React 16 and later, Error Boundaries are generally available to use. The common standard is to have at least one Error Boundary at the root of your application. It improves the user experience (UX) of your app and stops users from seeing blank or crashed pages.

Key takeaways:

Originally published on dev.to.


Mascot