Errors are bound to happen in our applications, whether they’re server-related errors, edge cases, or others. As such, many methods have been developed to prevent these errors from interfering with user and developer experience. In React, one of such method is the use of error boundaries.
Error boundaries in React
Error boundaries were introduced in React 16 as a way to catch and handle JavaScript errors that occur in the UI parts of our component. So error boundaries only catch errors that occur in a lifecycle method, render method, and inside Hooks like useEffect
. According to the React documentation, error boundaries do not handle errors in:
- Event handlers
- Asynchronous code (e.g.,
setTimeout
orrequestAnimationFrame
callbacks) - Server-side rendering
- Errors thrown in the error boundary itself (rather than its children)
So basically, error boundaries only handle errors in the parts of our code that involve React.
To create an error boundary, we simply have to create a class component and define a state variable for determining whether the error boundary has caught an error. Our class component should also have at least three methods:
- A static method called
getDerivedStateFromError
, which is used to update the error boundary’s state - A
componentDidCatch
lifecycle method for performing operations when our error boundaries catch an error, such as logging to an error logging service - A
render
method for rendering our error boundary’s child or the fallback UI in case of an error
Here’s an example (taken from the React docs) of what our simple error boundary should look like:
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch(error, errorInfo) { // You can also log the error to an error reporting service logErrorToMyService(error, errorInfo); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } }
react-error-boundary
react-error-boundary is a wrapper around React’s error boundary that allows us developers to implement error boundaries in our code without building our own from scratch. With react-error-boundary, we can simply wrap components where we expect errors with the provided ErrorBoundary
component and pass in some extra props to customize our error boundary’s behavior.
In this article, I will work through using react-error-boundary to deal with errors in a React application. Let’s take a look at what the library offers.
The ErrorBoundary
component
The ErrorBoundary
component is the main component available in react-error-boundary. It allows us to implement the typical React error boundary with less code. Here’s a very basic use case of ErrorBoundary
:
function App(){ ... return ( <ErrorBoundary FallbackComponent={OurFallbackComponent} > <ComponentThatMightThrowAnError/> </ErrorBoundary> ); } const OurFallbackComponent = ({ error, componentStack, resetErrorBoundary }) => { return ( <div> <h1>An error occurred: {error.message}</h1> <button onClick={resetErrorBoundary}>Try again</button> </div> ); };
In the above component, we simply wrap our component with the ErrorBoundary
component and pass in our fallback component to the FallbackComponent
prop so that if there’s an error (that can be caught and handled by react-error-boundary), our fallback component will be rendered; otherwise, our component will be rendered.
We also have the fallbackRender
prop, which is a render prop-based API for specifying our fallback component in an inline manner. Here’s the above code block using the fallbackRender
prop:
function App(){ ... return ( <ErrorBoundary fallbackRender = {({error, resetErrorBoundary, componentStack}) => ( <div> <h1>An error occurred: {error.message}</h1> <button onClick={resetErrorBoundary}>Try again</button> </div> )} > <ComponentThatMightThrowAnError/> </ErrorBoundary> ); }
The ErrorBoundary
also has an onError
prop, which acts as a listener that is triggered when our error boundary catches and handles an error in its child components. It is from this place that we might choose to log such errors to whatever error logging service we might be using.
function App(){ ... return ( <ErrorBoundary onError = {(error, componentStack) => { logToErrorLoggingService(error, componentStack); }} ... > <ComponentThatMightThrowAnError/> </ErrorBoundary> ); }
Resetting our error boundaries
react-error-boundary also provides a way for our code to recover from errors caught by our error boundaries. This is done using the reset keys and the resetErrorBoundary
function passed to the fallback component. The best way to explain how this works is to use an example code block taken directly from the documentation:
function ErrorFallback({error, componentStack, resetErrorBoundary}) { return ( <div role="alert"> <p>Something went wrong:</p> <pre>{error.message}</pre> <pre>{componentStack}</pre> <button onClick={resetErrorBoundary}>Try again</button> </div> ) } function Bomb() { throw new Error('💥 KABOOM 💥') } function App() { const [explode, setExplode] = React.useState(false) return ( <div> <button onClick={() => setExplode(e => !e)}>toggle explode</button> <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => setExplode(false)} resetKeys={[explode]} > {explode ? <Bomb /> : null} </ErrorBoundary> </div> ) }
As you can see from the code above, a state Hook was created and used to determine whether the App
component renders a Bomb
component that throws an error or an error-safe component. Reset keys were also passed to the error boundary component. These reset keys determine whether the error boundary’s internal state will be reset. If one of the reset keys change during renders, the error boundary’s internal state will be reset.
On the other hand, calling the resetComponent
function triggers the onReset
handler of our ErrorBoundary
component, where we set our explode
state value to false
. This causes our App
component to render an error-safe component.
We also have the onResetKeysChange
handler, which is triggered when the value of the reset keys change, causing a reset of the error boundary’s internal state.
The useErrorHandler
Hook
Another great feature of the react-error-boundary library is that it allows developers to use error boundaries to catch errors that wouldn’t otherwise be caught by traditional React error boundaries. This means we can now use error boundaries to catch errors during API requests, event handlers, and other parts of code where errors could occur.
This is how we would catch errors in an API request:
const App = () => { return ( <ErrorBoundary FallbackComponent={CharacterFallback} > <ComponentWhereErrorMightOccur/> </ErrorBoundary> ); }; const ComponentWhereErrorMightOccur = () => { const handleError = useErrorHandler(); const callAPI = () => { const result = fetch(apiURL) .then( (response) => response.json(), (error) => handleError(error)) .then((data) => { return data["results"]; }); return result; }; useEffect(() => { (async function () { await callAPI(); })(); }, []); return ( ... ); };
As you can see, all we need to do is pass the error object returned from fetching data from our API to our handleError
function, which was returned by the useErrorHandle
Hook. This way, our error boundaries are more useful.
Conclusion
react-error-boundary enables us React developers to reduce the amount of code we have to write and expand the capabilities of our error boundaries to catch other forms of errors that wouldn’t otherwise be caught by regular error boundaries. You can learn more about react-error-boundary here.
Full visibility into production React apps
Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your React apps — start monitoring for free.
Have you tried react-badly? Same idea as a wrapper around react error boundary