avatarJennifer Fu

Summary

The provided content offers a comprehensive guide on implementing error boundaries in React applications, detailing both custom error boundary components and the use of the react-error-boundary library.

Abstract

The text serves as an in-depth tutorial on managing errors in React applications through the use of error boundaries. It begins by acknowledging that software, including React applications, is prone to bugs that can cause components to unmount from the browser screen. The guide then introduces the concept of error boundaries as special React components that catch errors in their child components, log them, and display a fallback UI. The article explains how to write a custom error boundary using class components, demonstrates the use of the react-error-boundary library as an alternative, and discusses the limitations of error boundaries, such as their inability to catch errors in event handlers or asynchronous code. It also covers advanced topics, including error recovery techniques, handling specific errors, and dealing with errors outside the React component lifecycle. The guide concludes by inviting readers to consider their preferred approach to error handling in React.

Opinions

  • The author advocates for the use of error boundaries to enhance the robustness of React applications by preventing the entire app from crashing due to an error in a single component.
  • The preference for using react-error-boundary over custom error boundaries is implied, as it simplifies the implementation and provides additional features such as the useErrorBoundary hook and the withErrorBoundary higher-order component.
  • The author suggests that developers should not rely solely on error boundaries, as they have limitations and do not catch all types of errors (e.g., errors in event handlers or asynchronous code).
  • The article emphasizes the importance of error handling as a best practice in React development, suggesting that a well-designed application should include mechanisms to gracefully handle and recover from errors.
  • The author provides a pragmatic approach to error handling by discussing both theoretical aspects and practical implementation details, including code examples and screenshots.

Complete Guide on React Error Boundary

Write your own error boundary or use react-error-boundary

Photo by Gerald Berliner on Unsplash

The Problem

All software has bugs; it is normal.

React application errors could be JavaScript/TypeScript mistakes, lazy loading failing, or any kind of errors.

If encountering an error, a React application could be removed from the browser screen. We set up the Create React App to show how it happens.

The following command creates a React project:

% yarn create react-app react-error-boundary-app --template typescript
% cd react-error-boundary-app

Modify src/App.tsx to inject an error:

import logo from './logo.svg';
import './App.css';

function App(props: any) {
  props.map((a: any) => a); // this throws the error that props.map is not a function
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

Execute yarn start, and we see runtime errors on the browser. The following error screen is only visible in development mode during hot reload. It is react-error-overlay used by the Create React app.

Image by author

Errors not caught by error boundaries could unmount the React component tree. Closing the overlay with the X button at the top-right corner, we will see a blank screen.

react-error-overlay can be turned off by the Webpack configuration in webpack.config.js:

module.exports = {
  //...
  devServer: {
    overlay: false, // turn off react-error-overlay with one flag

    // or turn off react-error-overlay with the more granual setting
    // overlay: {
      // errors: false,
      // warnings: false,
      // runtimeErrors: false,
    // },
  },
};

For an un-ejected Create React app, the overlay can be hidden by hiding iframe with the following CSS styling in src/index.css:

body > iframe {
  display: none;
}

The Error Boundary

An error boundary is a special component that catches errors anywhere in its child component tree. It commonly logs errors and displays a fallback UI. Wrapping a React application in an error boundary or multiple error boundaries is recommended. There are two ways to create an error boundary:

  • Write your own error boundary.
  • Use react-error-boundary.

Both ways work effectively, and we will detail how they work.

Write Your Own Error Boundary

Writing an error boundary is standard practice. React document explains how it works and provides a comprehensive example.

The standard error boundary

Here is the standard error boundary code, which is added as src/ErrorBoundary.tsx:

import { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children?: ReactNode;
}

interface State {
  hasError: boolean;
}

class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

  // update state so the next render will show the fallback UI
  static getDerivedStateFromError(error: Error): State {
    console.error('getDerivedStateFromError is called:', error);
    // getDerivedStateFromError is called: TypeError: props.map is not a function
    //   at App (bundle.js:76:9)
    //   at renderWithHooks (react-dom.development.js:16305:1)
    //   at mountIndeterminateComponent (react-dom.development.js:20074:1)
    //   at beginWork (react-dom.development.js:21587:1)
    //   at beginWork$1 (react-dom.development.js:27426:1)
    //   at performUnitOfWork (react-dom.development.js:26557:1)
    //   at workLoopSync (react-dom.development.js:26466:1)
    //   at renderRootSync (react-dom.development.js:26434:1)
    //   at recoverFromConcurrentError (react-dom.development.js:25850:1)
    //   at performConcurrentWorkOnRoot (react-dom.development.js:25750:1)
    return { hasError: true };
  }

  // optionally to add some extra logic if needed
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Uncaught error:', error, errorInfo);
    // Uncaught error: TypeError: props.map is not a function
    //   at App (App.tsx:5:1)
    //   at renderWithHooks (react-dom.development.js:16305:1)
    //   at mountIndeterminateComponent (react-dom.development.js:20074:1)
    //   at beginWork (react-dom.development.js:21587:1)
    //   at beginWork$1 (react-dom.development.js:27426:1)
    //   at performUnitOfWork (react-dom.development.js:26557:1)
    //   at workLoopSync (react-dom.development.js:26466:1)
    //   at renderRootSync (react-dom.development.js:26434:1)
    //   at recoverFromConcurrentError (react-dom.development.js:25850:1)
    //   at performConcurrentWorkOnRoot (react-dom.development.js:25750:1) 
    // 
    // {componentStack: '\n    at App (http://localhost:3000/static/js/bundl…(http://localhost:3000/static/js/bundle.js:180:5)'}
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong...</h1>; // display a fallback UI
    }

    return this.props.children; // display the normal UI
  }
}

export default ErrorBoundary;

The above code is written as a class component, and there is no way to write an error boundary as a function component.

Change src/index.tsx to wrap <App /> with an ErrorBoundary:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import ErrorBoundary from './ErrorBoundary';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <div>Other Part of UI still works</div>
    <ErrorBoundary>
      <App />
    </ErrorBoundary>
    
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Execute yarn start, and we see the application shows a fallback UI.

Image by author

Outside of the error boundary, another part of the UI works as usual.

An application can have multiple error boundaries. Everything inside the React.StrictMode component can be wrapped by another error boundary.

Special cases in an error boundary

We can also have a special fallback UI for the specific error, props.map is not a function — displaying nothing:

import { Component, ReactNode } from 'react';

interface Props {
  children?: ReactNode;
}

interface State {
  error: string;
}

class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { error: '' };
  }

  static getDerivedStateFromError(error: Error) {
    return { error: error.message };
  }

  render() {
    if (/props\.map is not a function/.test(this.state.error)) {
      return null;
    } else if (this.state.error) {
      return <h1>Something went wrong...</h1>;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

The above code retains the error message as a state, { error: error.message } instead of a boolean value. This approach enables additional processing based on the error text.

Execute yarn start, and we see the blank fallback UI for the uncaught error that props.map is not a function.

Image by author

The error boundary that ignores harmless errors

As some errors are harmless, we can simply ignore them in render():

render() {
  if (this.state.error && !/js-agent\.newrelic\.com|reading 'parentElement'/.test(this.state.error)) {
    return <h1>Something went wrong...</h1>;
  }

  return this.props.children;
}

There are two errors ignored:

  • js-agent.newrelic.com: It is part of this.state.error.toString() when an ad blocker detects newrelic code for tracking and analytics.
  • reading ‘parentElement’: It is part of this.state.error.toString() thrown occasionally from the Ant Design System.

Sometimes, we may not be able to rely on this.state.error.toString(). For example, the following Unhandled Promise Rejection: Failed to fetch is a common error is too common, and we have to check this.state.error.stack to ignore a specific error thrown from newrelic.

Uncaught (in promise) TypeError: Unhandled Promise Rejection: Failed to fetch
    at e.<computed> (vdsfdsdfdsf:2:16380)
    at o (js-agent.newrelic.com/async-api.30bd804e-1.236.0.min.js:2:1266)
    at C._send (js-agent.newrelic.com/148.1a20d5fe-1.236.0.min.js:2:3697)
    at C.sendX (js-agent.newrelic.com/148.1a20d5fe-1.236.0.min.js:2:1787)
    at r (js-agent.newrelic.com/ajax-aggregate.998ef92b-1.236.0.min.js:1:2176)
    at js-agent.newrelic.com/ajax-aggregate.998ef92b-1.236.0.min.js:1:2289
    at Array.forEach (<anonymous>)
    at d.runHarvest (js-agent.newrelic.com/ajax-aggregate.998ef92b-1.236.0.min.js:1:2276)
    at d.unload (js-agent.newrelic.com/ajax-aggregate.998ef92b-1.236.0.min.js:1:1330)
    at HTMLDocument.<anonymous> (vdsfdsdfdsf:2:13714)

Although error.stack is non-standard and is not on a standards track, most browsers support it. We can use the condition, error.stack && this.state.error.stack.includes('js-agent.newrelic.com'), to ignore this error.

The error boundary that reloads the code

Occasionally, React/Webpack throws an error, Loading chunk 4 failed. The actual chunk id could be different for each case. The application needs a reload to recover. This can be implemented automatically:

render() {
  if (this.state.error) {
    // recover from the chunk loading error
    if (/Loading chunk [\d]+ failed/.test(this.state.error)) {
      console.error(`reload to recover from the error: ${this.state.error}`);
      const lastReloadTime = localStorage.getItem('reload-to-recover-time');
      if (!lastReloadTime || Date.now() - Number(lastReloadTime) > 300000) { // 5 minutes
        localStorage.setItem('reload-to-recover-time', String(Date.now()));
        window.location.reload();
        return;
      }
    }

    return <h1>Something went wrong...</h1>;
  }

  return this.props.children;
}

The above code saves the reload timestamp in the local storage, which controls the minimal reload interval (300,000 milliseconds = 5 minutes).

The error boundary that recovers from an error

An error boundary takes a unique key prop. When the key changes, the error boundary will be unmounted and remounted. It could make the application recover from the error state.

Here is the modify src/App.tsx:

import { useState } from 'react';
import ErrorBoundary from './ErrorBoundary';

const RandomNumber = ({ value }: { value: number }) => {
  // there is 50% chance that the component crashes
  if (value > 0.5) {
    throw new Error('Crash');
  }
  return <p>The random number is {value}</p>;
};

const App = () => {
  const [value, setValue] = useState(0);
  return (
    <>
      <button onClick={() => setValue(Math.random())}>click</button>
      <ErrorBoundary key={value}> // a unique key prop to recover
        <RandomNumber value={value} />
      </ErrorBoundary>
    </>
  );
};

export default App;

Execute yarn start, and there is a 50% chance that the component crashes. We can see the fallback UI and application recover from an error state by clicking the button. The action triggers the remount of the error boundary:

Image by author

Error boundary limitations

Error boundaries have limitations. The React documentation states that error boundaries do not catch errors for the following cases:

  • Event handlers
  • Asynchronous code (e.g., setTimeout or requestAnimationFrame callbacks)
  • Server-side rendering
  • Errors are thrown in the error boundary itself (rather than its children)

Use react-error-boundary

It is easy to write an error boundary, and it is easier to use an existing error boundary. react-error-boundary is a popular React error boundary component.

Install the package with this command:

% yarn add react-error-boundary

After the installation, react-error-boundary becomes part of dependencies in package.json:

"dependencies": {
  "react-error-boundary": "^4.0.10"
}

The following is the type definition of the builtin ErrorBoundary, which looks similar to the class component we created.

import { Component, ErrorInfo, PropsWithChildren, PropsWithRef } from "react";
import { ErrorBoundaryProps } from "./types.js";
type ErrorBoundaryState = {
    didCatch: boolean;
    error: any;
};
export declare class ErrorBoundary extends Component<PropsWithRef<PropsWithChildren<ErrorBoundaryProps>>, ErrorBoundaryState> {
    constructor(props: ErrorBoundaryProps);
    static getDerivedStateFromError(error: Error): {
        didCatch: boolean;
        error: Error;
    };
    resetErrorBoundary(...args: any[]): void;
    componentDidCatch(error: Error, info: ErrorInfo): void;
    componentDidUpdate(prevProps: ErrorBoundaryProps, prevState: ErrorBoundaryState): void;
    render(): import("react").FunctionComponentElement<import("react").ProviderProps<import("./ErrorBoundaryContext.js").ErrorBoundaryContextType | null>>;
}
export {};

It supports three types of error boundaries, ErrorBoundaryPropsWithFallback, ErrorBoundaryPropsWithRender, and ErrorBoundaryPropsWithComponent.

export type ErrorBoundaryProps = ErrorBoundaryPropsWithFallback | ErrorBoundaryPropsWithComponent | ErrorBoundaryPropsWithRender;

Let’s take a look at the detail.

ErrorBoundaryPropsWithFallback

Here is the definition of ErrorBoundaryPropsWithFallback, where never indicates a value that will never occur.

export type ErrorBoundaryPropsWithFallback = ErrorBoundarySharedProps & {
  fallback: ReactElement<unknown, string | FunctionComponent | typeof Component> | null;
  FallbackComponent?: never;
  fallbackRender?: never;
};

Our recoverable error boundary can be implemented by react-error-boundary using the fallback prop.

Here is the modified src/App.tsx:

import { useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

const RandomNumber = ({ value }: { value: number }) => {
  if (value > 0.5) {
    throw new Error('Crash');
  }
  return <p>The random number is {value}</p>;
};

const App = () => {
  const [value, setValue] = useState(0);
  return (
    <>
      <button onClick={() => setValue(Math.random())}>click</button>
      <ErrorBoundary key={value} fallback={<h1>Something went wrong...</h1>}>
        <RandomNumber value={value} />
      </ErrorBoundary>
    </>
  );
};

export default App;

ErrorBoundaryPropsWithRender

Here is the definition of ErrorBoundaryPropsWithRender:

export type ErrorBoundaryPropsWithRender = ErrorBoundarySharedProps & {
  fallback?: never;
  FallbackComponent?: never;
  fallbackRender: typeof FallbackRender;
};

We actually should not use ErrorBoundary’s key prop to recover from the error. The following ErrorBoundarySharedProps shows that ErrorBoundary itself can reset:

type ErrorBoundarySharedProps = {
  onError?: (error: Error, info: {
    componentStack: string;
  }) => void;
  onReset?: (details: {
    reason: "imperative-api";
    args: any[];
  } | {
    reason: "keys";
    prev: any[] | undefined;
    next: any[] | undefined;
  }) => void;
  resetKeys?: any[];
};

The fallbackRender prop is a render prop that is invoked by two parameters, error and resetErrorBoundary. resetErrorBoundary is a function that triggers a reset.

export type FallbackProps = {
  error: any;
  resetErrorBoundary: (...args: any[]) => void;
};

Upon calling resetErrorBoundary, onReset is invoked, where we can reset the app to a good value or reload the app.

Here is the modified src/App.tsx:

import { useState } from 'react';
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';

const RandomNumber = ({ value }: { value: number }) => {
  if (value > 0.5) {
    throw new Error('Crash');
  }
  return <p>The random number is {value}</p>;
};

function fallbackRender({ error, resetErrorBoundary }: FallbackProps) {
  console.error(error.message);
  return (
    <div role="alert">
      <h1>Something went wrong...</h1>
      <button onClick={resetErrorBoundary}>try again</button>
    </div>
  );
}

const App = () => {
  const [value, setValue] = useState(0);
  return (
    <>
      <button onClick={() => setValue(Math.random())}>click</button>
      <ErrorBoundary
        fallbackRender={fallbackRender}
        onReset={(details) => {
          console.log(details);
          // {args: [SyntheticBaseEvent], reason: 'imperative-api'}

          // reset to a good value
          setValue(0);
          // or reload the app
          // window.location.reload();
        }}
      >
        <RandomNumber value={value} />
      </ErrorBoundary>
    </>
  );
};

export default App;

Execute yarn start. When the component crashes, there is a button, try again, to recover from the error state.

Image by author

ErrorBoundaryPropsWithComponent

Here is the definition of ErrorBoundaryPropsWithComponent:

export type ErrorBoundaryPropsWithComponent = ErrorBoundarySharedProps & {
  fallback?: never;
  FallbackComponent: ComponentType<FallbackProps>;
  fallbackRender?: never;
};

ErrorBoundaryPropsWithComponent has all the capability that ErrorBoundaryPropsWithRender has, except that it is a component instead of a render function.

Here is the modified src/App.tsx:

import { useState } from 'react';
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';

const RandomNumber = ({ value }: { value: number }) => {
  if (value > 0.5) {
    throw new Error('Crash');
  }
  return <p>The random number is {value}</p>;
};

function Fallback({ error, resetErrorBoundary }: FallbackProps) {
  console.error(error.message);
  return (
    <div role="alert">
      <h1>Something went wrong...</h1>
      <button onClick={resetErrorBoundary}>try again</button>
    </div>
  );
}

const App = () => {
  const [value, setValue] = useState(0);
  return (
    <>
      <button onClick={() => setValue(Math.random())}>click</button>
      <ErrorBoundary
        FallbackComponent={Fallback}
        onReset={(details) => {
          console.log(details);
          // {args: [SyntheticBaseEvent], reason: 'imperative-api'}

          // reset to a good value
          setValue(0);
          // or reload the app
          // window.location.reload();
        }}
      >
        <RandomNumber value={value} />
      </ErrorBoundary>
    </>
  );
};

export default App;

Logging errors with onError

We have seen the definition of onError from ErrorBoundarySharedProps.

onError?: (error: Error, info: {
  componentStack: string;
}) => void;

onError is invoked with similar parameters as componentDidCatch.

Here is the ErrorBoundary with the onError prop:

<ErrorBoundary
  FallbackComponent={Fallback}
  onReset={(details) => {
    console.log(details);
    // {args: [SyntheticBaseEvent], reason: 'imperative-api'}

    // reset to a good value
    setValue(0);
    // or reload the app
    // window.location.reload();
    }}
    onError={(error: Error, info: { componentStack: string }) => {
      console.error('error', error);
      // error Error: Crash
      //   at RandomNumber (App.tsx:6:1)
      //   at renderWithHooks (react-dom.development.js:16305:1)
      //   at updateFunctionComponent (react-dom.development.js:19588:1)
      //   at beginWork (react-dom.development.js:21601:1)
      //   at beginWork$1 (react-dom.development.js:27426:1)
      //   at performUnitOfWork (react-dom.development.js:26557:1)
      //   at workLoopSync (react-dom.development.js:26466:1)
      //   at renderRootSync (react-dom.development.js:26434:1)
      //   at recoverFromConcurrentError (react-dom.development.js:25850:1)
      //   at performSyncWorkOnRoot (react-dom.development.js:26096:1) 
      //   at ErrorBoundary (http://localhost:3000/static/js/bundle.js:32944:5)
      //   at App (http://localhost:3000/main.9d4b79bf702600a2d8a7.hot-update.js:72:76)
          
      console.info('info', info);
      // {componentStack: '\n    at RandomNumber (http://localhost:3000/main.9…00/main.9d4b79bf702600a2d8a7.hot-update.js:72:76)'}
  }}
>
  <RandomNumber value={value} />
</ErrorBoundary>

useErrorBoundary hook

react-error-boundary provides the useErrorBoundary hook, which includes two methods, resetBoundary and showBoundary.

export type UseErrorBoundaryApi<Error> = {
    resetBoundary: () => void;
    showBoundary: (error: Error) => void;
};
export declare function useErrorBoundary<Error = any>(): UseErrorBoundaryApi<Error>;
  • resetBoundary: It requests the nearest error boundary to retry the failed render.

Here is an example of resetBoundary:

function Fallback({ error }: FallbackProps) {
  console.error(error.message);
  const { resetBoundary } = useErrorBoundary();
  return (
    <div role="alert">
      <h1>Something went wrong...</h1>
      <button onClick={resetBoundary}>try again</button>
    </div>
  );
}
  • showBoundary: It requests the nearest error boundary to show the received error.

React only handles errors thrown during render or component lifecycle methods. Errors thrown in event handlers and asynchronous code will not be caught.

The following example shows that the error in setTimeout() will not be caught.

import { useState } from 'react';
import {
  ErrorBoundary,
  FallbackProps,
} from 'react-error-boundary';

const RandomNumber = ({ value }: { value: number }) => {
  if (value > 0.5) {
    setTimeout(() => {
      new Error('Crash');
    }, 0);
  }
  return <p>The random number is {value}</p>;
};

function fallbackRender({ error, resetErrorBoundary }: FallbackProps) {
  console.error(error.message);
  return (
    <div role="alert">
      <h1>Something went wrong...</h1>
      <button onClick={resetErrorBoundary}>try again</button>
    </div>
  );
}

const App = () => {
  const [value, setValue] = useState(0);
  return (
    <>
      <button onClick={() => setValue(Math.random())}>click</button>
      <ErrorBoundary
        fallbackRender={fallbackRender}
        onReset={() => window.location.reload()}
      >
        <RandomNumber value={value} />
      </ErrorBoundary>
    </>
  );
};

export default App;

However, showBoundary can be used to catch errors to the nearest error boundary.

Here is an example of showBoundary:

import { useState } from 'react';
import {
  ErrorBoundary,
  FallbackProps,
  useErrorBoundary,
} from 'react-error-boundary';

const RandomNumber = ({ value }: { value: number }) => {
  const { showBoundary } = useErrorBoundary();
  if (value > 0.5) {
    setTimeout(() => {
      showBoundary(new Error('Crash'));
    }, 0);
  }
  return <p>The random number is {value}</p>;
};

function fallbackRender({ error, resetErrorBoundary }: FallbackProps) {
  console.error(error.message);
  return (
    <div role="alert">
      <h1>Something went wrong...</h1>
      <button onClick={resetErrorBoundary}>try again</button>
    </div>
  );
}

const App = () => {
  const [value, setValue] = useState(0);
  return (
    <>
      <button onClick={() => setValue(Math.random())}>click</button>
      <ErrorBoundary
        fallbackRender={fallbackRender}
        onReset={() => window.location.reload()}
      >
        <RandomNumber value={value} />
      </ErrorBoundary>
    </>
  );
};

export default App;

Execute yarn start, and the error in setTimeout() is caught by the nearest error boundary.

Image by author

withErrorBoundary HOC

react-error-boundary can also be used as a higher-order component that accepts all of the same props of ErrorBoundary:

Here is the definition of withErrorBoundary.

import { RefAttributes, ForwardRefExoticComponent, PropsWithoutRef, ComponentType } from "react";
import { ErrorBoundaryProps } from "./types.js";
export declare function withErrorBoundary<Props extends Object>(component: ComponentType<Props>, errorBoundaryProps: ErrorBoundaryProps): ForwardRefExoticComponent<PropsWithoutRef<Props> & RefAttributes<any>>;

We can use withErrorBoundary to wrap a component with an error boundary:

import logo from './logo.svg';
import './App.css';
import { withErrorBoundary } from 'react-error-boundary';

function App(props: any) {
  props.map((a: any) => a); //this throws the error that props.map is not a function
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default withErrorBoundary(App, {
  fallback: <h1>Something went wrong...</h1>,
  onError(error, info) {
    console.error('error', error);
    console.info('info', info);
  },
});

Execute yarn start, and it shows the fallback UI:

Image by author

Conclusion

We have explained that an error boundary is a special component that catches errors anywhere in its child component tree. It commonly logs errors and displays a fallback UI. Wrapping a React application in an error boundary or multiple error boundaries is recommended.

We have walked through details on two ways to create an error boundary:

  • Write your own error boundary.
  • Use react-error-boundary.

Both ways work effectively. What is your choice?

Thanks for reading.

Thanks, S Sreeram and Pendri Laxmi Prasanna, for working with me on error boundary.

Want to Connect?

If you are interested, check out my directory of web development articles.
React
Error Handling
Libraries
Programming
Web Development
Recommended from ReadMedium