Robust apps fail gracefully per section instead of showing a blank page or a generic crash.
Next.js separates two kinds of errors.
Expected errors (validation failures, a 404 from an API) are part of normal operation. Model them as return values: render a message, or return an error object from a Server Action and show it with useActionState. Don't throw for these.
Uncaught exceptions are bugs or outages. Throw, and let an error boundary catch them:
error.tsxin a segment wraps it in an error boundary. It must be a Client Component and receiveserror(the thrown value, typedunknown) andretry, which re-fetches and re-renders the segment. Errors bubble up to the nearesterror.tsx, so the parent layout and navigation keep working.- Errors from Server Components reach the client with the message stripped in production; use
error.digestto match server logs. global-error.tsxhandles errors in the root layout and must render its own<html>and<body>.catchErrorfromnext/errorcreates reusable component-level error boundaries.- Error boundaries don't catch errors in event handlers; handle those with try/catch and state.
Not found: call notFound() from next/navigation when a resource doesn't exist. It renders the nearest not-found.tsx and sets a 404 status. app/not-found.tsx also handles unmatched URLs.
Example
// app/dashboard/error.tsx
'use client' // error boundaries are Client Components
import { useEffect } from 'react'
export default function DashboardError({ error, retry }: { error: unknown; retry: () => void }) {
useEffect(() => {
console.error(error) // report to your error tracker
}, [error])
const message = error instanceof Error ? error.message : 'Unknown error'
return (
<div>
<h2>Something went wrong: {message}</h2>
<button onClick={() => retry()}>Try again</button>
</div>
)
}
// app/blog/[slug]/page.tsx
// const post = await getPost(slug)
// if (!post) notFound() -> renders app/blog/[slug]/not-found.tsx
// app/blog/[slug]/not-found.tsx
// export default function NotFound() { return <p>No such post.</p> }Remember
- Return expected errors as values; throw only for unexpected failures
error.tsxis a client error boundary per segment witherrorandretrynotFound()renders the nearestnot-found.tsxwith a 404 status
Watch out for
- Writing
error.tsxwithout'use client', which fails because error boundaries need client-side React.