š”ļø Error Boundaries in React: Catching Mistakes Before They Crash Your App
Even the most careful developers canāt avoid bugs. Whether itās a typo, a broken API call, or a rendering error, things will go wrong eventually. But when they do, whatās your app going to show?
If the answer is a blank screen, your users wonāt be too happy.
Luckily, React gives us a way to gracefully handle these situations using Error Boundaries.
Letās say you have a simple app:
const element = (<div><h1>Calculator</h1><Calculator left={1} operator="+" right={2} /></div>)
At this point, React has not actually executed the Calculator
functionāitās just stored a reference to it. That means if thereās an error inside Calculator
, wrapping the JSX in a try/catch
wonāt help.
So you might think to do this:
function Calculator({ left, operator, right }) {try {const result = operations[operator](left, right)return <div>{result}</div>} catch (error) {return <div>Oops!</div>}}
This worksābut is ugly and not scalable. Imagine doing that for every component!
What if you could do this?
<ErrorBoundary fallback={<div>Oops! Something went wrong.</div>}><App /></ErrorBoundary>
You can! This is where Error Boundaries shine.
Error Boundaries are special components that catch rendering errors in their child components. Think of them like Reactās version of try/catch
.
React doesnāt ship Error Boundaries out-of-the-box, but you can make one with a class 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}}
Usage:
<ErrorBoundary fallback={<div>Something went wrong.</div>}><App /></ErrorBoundary>
ā ļø Functional components cannot be Error Boundaries (at least not yet).
react-error-boundary
For most apps, itās best to use the react-error-boundary package. Itās simple, powerful, and production-ready.
npm install react-error-boundary
function ErrorFallback({ error, resetErrorBoundary }) {return (<div role="alert"><p>Something went wrong:</p><pre>{error.message}</pre><button onClick={resetErrorBoundary}>Try again</button></div>)}
Letās say you have this:
const root = createRoot(document.getElementById('root'))root.render(<App />)
Update it like this:
function OnboardingForm() {// your original App logic here}function App() {return (<ErrorBoundary FallbackComponent={ErrorFallback}><OnboardingForm /></ErrorBoundary>)}
Error Boundaries only catch rendering errors. What if the error happens in an event handler or useEffect
?
React canāt catch these on its own because theyāre outside its render cycle. But react-error-boundary
gives us a handy hook: useErrorBoundary
.
import { useErrorBoundary } from 'react-error-boundary'function App() {const { showBoundary } = useErrorBoundary()useEffect(() => {function handleMouseMove(event) {try {// Some risky logic here} catch (error) {showBoundary(error)}}window.addEventListener('mousemove', handleMouseMove)return () => window.removeEventListener('mousemove', handleMouseMove)}, [])return <div>Move your mouse!</div>}
Letās say your onSubmit
handler has a typo:
function onSubmit(event) {event.preventDefault()const name = nullconsole.log(name.toUpperCase()) // š„ Uncaught TypeError}
To handle this cleanly:
const { showBoundary } = useErrorBoundary()function onSubmit(event) {event.preventDefault()try {const name = nullconsole.log(name.toUpperCase())} catch (error) {showBoundary(error)}}
Just like try/catch
, you donāt want one giant Error Boundary for your whole app.
Instead, place them where theyāll be the most helpful.
For example:
function App() {return (<div><ErrorBoundary fallback={<div>Problem loading the list.</div>}><List /></ErrorBoundary><ErrorBoundary fallback={<div>Problem loading the item.</div>}><Detail /></ErrorBoundary></div>)}
This way, if Detail
fails, users can still see and interact with the List
.
Sometimes, errors are temporary. Maybe the network glitched or data was missing. Letting users try again is easy with resetErrorBoundary
.
Youāve already seen it in the ErrorFallback
example above:
function ErrorFallback({ error, resetErrorBoundary }) {return (<div role="alert"><p>{error.message}</p><button onClick={resetErrorBoundary}>Try again</button></div>)}
Clicking the button resets the error boundary and re-renders the children.
Error Boundaries are a powerful pattern for improving UX in React apps. Hereās what to remember:
try/catch
blocksāclose to where the error may occur.react-error-boundary
for a better developer experience.useErrorBoundary
to surface them.With a bit of planning, you can turn a crashing app into one that fails gracefully and keeps your users happy!
Quick Links
Legal Stuff
Social Media