Error boundaries
Catch rendering errors, show fallback UI, reset after errors and report them.
Bugs happen. An API returns something unexpected, a value is undefined where you expected an object, and suddenly a component throws. By default, a rendering error anywhere unmounts your whole React app — the user sees a blank white page. Error boundaries catch those errors and show a fallback UI for just the broken part, so the rest of the app keeps working.
Seeing the problem#
Header and Footer vanish too. React does this on purpose: showing a half-broken UI could be worse than showing nothing (imagine a banking app displaying the wrong balance). But we can do better than a blank page.
Writing an error boundary#
Error boundaries are one of the few things that still need a class component, because they use two class-only lifecycle methods:
Use it like any component:
Now only the profile area shows the fallback; the header and footer stay put.
The react-error-boundary package#
You'll rarely write the class yourself. The popular react-error-boundary package gives you a flexible, well-tested boundary:
There are three ways to render the fallback:
fallback={<p>Oops</p>}— static JSX.FallbackComponent={ErrorFallback}— a component receiving{ error, resetErrorBoundary }.fallbackRender={({ error, resetErrorBoundary }) => ...}— an inline render function.
error can be anything that was thrown (not always an Error), so the package's getErrorMessage(error) helper safely extracts a message.
Resetting automatically with resetKeys#
If the error came from specific data, moving to different data should clear it:
When userId changes, the boundary resets and tries rendering the new profile. (Giving the boundary key={userId} has a similar effect, but also remounts the children.)
What error boundaries catch — and what they don't#
Boundaries catch errors thrown while React is rendering the tree below them:
- ✅ During rendering (
user.profile.nameonundefined) - ✅ In lifecycle methods and effects (
useEffect,useLayoutEffect) - ✅ In constructors of child class components
- ✅ Errors from rejected promises read with
use()or thrown by Suspense-enabled data libraries - ✅ Errors thrown inside a
startTransitionAction (React 19)
They do not catch errors:
- ❌ In event handlers (
onClick) — these happen outside rendering - ❌ In async code (
setTimeout, a.thencallback that isn't part of rendering) - ❌ During server-side rendering
- ❌ Inside the error boundary itself (a parent boundary handles those)
Errors in event handlers and async code#
For event handlers, a regular try/catch and some state is usually best:
If the error should show the boundary's fallback instead, hand it over with useErrorBoundary:
In React 19, errors thrown inside a startTransition callback (from useTransition) are also sent to the nearest boundary automatically.
Where to put boundaries#
Think about the user experience: what's the smallest piece of UI that can fail on its own?
- A top-level boundary avoids blank screens.
- Per-feature boundaries keep independent widgets (comments, charts, sidebars) from taking each other down.
- With React Router, each route's
ErrorBoundaryacts as a per-page boundary.
Don't wrap every tiny component — too many boundaries make failures confusing.
Global error reporting in React 19#
createRoot accepts callbacks that fire for every error React handles — ideal for sending them to a monitoring service such as Sentry:
Development vs production#
In development, Vite shows an error overlay and React logs the error even when a boundary catches it — that's expected. To see what users will see, close the overlay or test a production build with npm run build && npm run preview.
Common mistakes#
- Expecting a boundary to catch errors from
onClickhandlers orsetTimeout. - Having no top-level boundary, so any bug becomes a blank page.
- Showing technical stack traces to users — show a friendly message and log details.
- A "Try again" button that re-renders with the same broken data. Reset the cause (
onReset,resetKeys) too. - Assuming
erroris always anErrorinstance.
What's next#
Error boundaries handle failure; Suspense handles waiting. Next: Suspense, lazy loading and transitions.
Check your understanding
Quick quiz
1.What happens by default in React when a component throws an error during rendering and there's no error boundary?
2.Which error will an error boundary NOT catch on its own?
3.In
react-error-boundary, what doesresetKeys={[userId]}do?
Finished reading?
Mark this lesson complete to track your progress.