Error Boundaries
In this tutorial, you will learn how Error Boundaries work in React 19, why they exist, and how they fit together with Suspense and the use API.
Error boundaries are the safety net of modern React applications.
What We Will Learn
- What an error boundary is
- What kinds of errors they catch
- How error boundaries work with
useand Suspense - Where error boundaries should live
- Common mistakes and best practices
By the end, you will know exactly how to protect your app from crashes without cluttering your components.
What Is an Error Boundary
An error boundary is a component that catches runtime errors during rendering and shows a fallback UI instead of crashing the entire app.
Think of it like this:
If something goes wrong in this part of the UI, show a safe message instead of breaking everything.
Why Error Boundaries Exist
Without error boundaries:
- A single rendering error crashes the whole app
- Users see a blank screen
- Debugging becomes painful
Error boundaries allow React to:
- Isolate failures
- Recover gracefully
- Keep the rest of the UI working
Error Boundaries in React 19
In React 19 (and modern frameworks like App Router), error boundaries are file based.
You do not manually write class components anymore.
Instead, you create an error.tsx file.
Basic Error Boundary Example
Error Boundary File
// app/error.tsx
"use client"
export default function Error({
error,
reset
}: {
error: Error
reset: () => void
}) {
return (
<div>
<h2>Something went wrong</h2>
<p>{error.message}</p>
<button onClick={reset}>
Try again
</button>
</div>
)
}This file automatically becomes an error boundary for the route.
What This Error Boundary Catches
An error boundary catches:
- Errors thrown during render
- Errors thrown by
use(promise) - Errors thrown by server components
- Errors thrown while streaming UI
It does not catch:
- Event handler errors
- Async errors outside render
- Errors in timers or effects
Those should be handled locally.
Error Boundaries and use
When using the use API, errors are expected and intentional.
Example:
// lib/get-user.ts
export async function getUser() {
const res = await fetch("/api/user")
if (!res.ok) {
throw new Error("Failed to load user")
}
return res.json()
}// components/user-profile.tsx
import { use } from "react"
import { getUser } from "../lib/get-user"
export function UserProfile() {
const user = use(getUser())
return <p>{user.name}</p>
}If getUser() fails:
- The Promise rejects
- React throws the error
- The nearest error boundary renders
No try/catch needed.
Error Boundaries vs Suspense
This is a common source of confusion.
| Feature | Purpose |
|---|---|
| Suspense | Handles loading states |
| Error Boundary | Handles failures |
They work together but solve different problems.
Using Suspense and Error Boundaries Together
A typical structure looks like this:
// app/page.tsx
import { Suspense } from "react"
import { UserProfile } from "../components/user-profile"
export default function Page() {
return (
<>
<title>Error Boundaries</title>
<meta name="description" content="React 19 error boundary tutorial" />
<Suspense fallback={<p>Loading user...</p>}>
<UserProfile />
</Suspense>
</>
)
}- While loading: Suspense fallback
- On failure: error boundary UI
- On success: real content
Each concern is cleanly separated.
Resetting an Error Boundary
React provides a reset function to retry rendering.
This is useful for:
- Temporary network failures
- Retry buttons
- Recoverable errors
<button onClick={reset}>
Retry
</button>Calling reset re-attempts rendering from scratch.
Where Error Boundaries Should Live
Good places:
- Route level
- Feature level
- Data heavy sections
Avoid:
- Wrapping everything in one global error boundary
- Catching errors too low in the tree unless necessary
Error boundaries should match logical failure zones.
Common Mistakes
Trying to Catch Errors with try/catch
try {
const data = use(fetchData())
} catch {
// This will not work
}Rendering errors must be handled by error boundaries, not try/catch.
Using Error Boundaries for Loading
Error boundaries are not for loading UI.
Use Suspense for loading and error boundaries for failures.
Mental Model
Think of error boundaries like circuit breakers:
- Something breaks
- The breaker trips
- Power is cut only to that section
- The rest of the system keeps running
This keeps your app resilient.
When You Should Use Error Boundaries
You should always have error boundaries when:
- Using
usefor data fetching - Streaming UI
- Rendering server components
- Building production apps
They are not optional in modern React.
Conclusion
Error boundaries are the last line of defense in React 19.
They work hand in hand with Suspense and the use API to make apps:
- More resilient
- Easier to reason about
- Safer in production
Once you rely on boundaries instead of defensive code everywhere, your components become simpler and more focused.