HomeAbout Me

React Fundamental 9: Error Boundaries

By Daniel Nguyen
Published in React JS
May 09, 2025
2 min read
React Fundamental 9: Error Boundaries

The problem is, you can’t just wrap your entire application in a try/catch because of the way JavaScript works.

If we tried to wrap our JSX in a try/catch, it would only catch errors during the creation of those elements, but the real errors will happen with React gets around to calling our component functions.

So you could wrap all of the contents of your components in a try/catch, but that’s super annoying boilerplate:

// no, don't do this...
function Calculator({ left, operator, right }) {
try {
const result = operations[operator](left, right)
return (
<div>
<code>
{left} {operator} {right} = <output>{result}</output>
</code>
</div>
)
} catch (error) {
return <div>Oh no! An error occurred!</div>
}
}
// ugh, that would be terrible

They’re called Error Boundaries.

So you can make a special kind of component that implements the Error Boundary API, and then use it like so:

<ErrorBoundary fallback={<div>Oh no!</div>}>
<App />
</ErrorBoundary>

And just like with try/catch you can place these error boundaries wherever you want to catch errors. You can even nest them to catch errors in different parts of your application to give your users a more meaningful experience.

Unfortunately, React doesn’t ship this component as a built-in component, and there is currently no way to create an Error Boundary component as a modern function component so you have to use a class component instead.

Here’s a simple version of an Error Boundary component:

class ErrorBoundary extends React.Component {
state = { error: null }
static getDerivedStateFromError(error) {
return { error }
}
render() {
return this.state.error ? this.props.fallback : this.props.children
}
}

Then you’d use it like so:

<ErrorBoundary fallback={<div>Oh no!</div>}>
<App />
</ErrorBoundary>

And now any errors thrown in App will be caught by the Error Boundary and render out the error message instead of crashing the app.

You’re never going to need to do this though. Instead we’ll follow the official docs’ recommendation and use react-error-boundary which has a really nice API with a variety of ways to use it for different use cases. Check 📜 the docs for more details.

Async Errors

In React, you need to consider two types of errors: errors that happen during rendering, and errors that don’t (like an event handler, a useEffect callback, or a promise .then/.catch).

Under the hood, React is using try/catch to catch errors that happen during rendering, but React cannot catch all of these kinds of errors because they happen outside of React’s call stack.

So you need to surface those to React yourself. It’s not very often you’ll need to handle these kinds of errors in real world applications using modern React patterns and frameworks, but you may run into cases where it’s useful. The easiest way to do this is to use the useErrorBoundary hook from react-error-boundary:

function App() {
const { showBoundary } = useErrorBoundary()
useEffect(() => {
function handleMouseMove(event) {
try {
// Do something that could throw
} catch (error) {
showBoundary(error)
}
}
window.addEventListener('mousemove', handleMouseMove)
return () => {
window.removeEventListener('mousemove', handleMouseMove)
}
})
// Render ...
}

An alternative to using an error boundary for this type of error is to store the error in state and display that. Both approaches are valid, and which one you choose comes down to which feels better. I typically try both and choose the one I hate the least 😅

Localized Error Handling

Most applications will use more than a single error boundary. You should really think about error boundaries as try/catch blocks. You don’t want to wrap your entire application in a single try/catch block because it’s more difficult to handle errors in a meaningful way. Instead, you wrap things in more practical places so you can handle errors in a more localized way with the kind of context that makes sense for that part of the application.

You’ll use error boundaries in a similar way. Just think about what the user will be able to do when errors occur and if you’re able to give them more useful actions by handling the error more closely to where it originated, then do that.

function App() {
return (
<div>
<ErrorBoundary fallback={<div>Something went wrong with the list.</div>}>
<List />
</ErrorBoundary>
<ErrorBoundary
fallback={<div>Something went wrong with this item, try another.</div>}
>
<Detail />
</ErrorBoundary>
</div>
)
}

Tags

#ReactFundamental

Share

Previous Article
📘 Section 41: Define Scope

Table Of Contents

1
Async Errors
2
Localized Error Handling

Related Posts

React Fundamental Section 10: Rendering Arrays
May 10, 2025
1 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media