Type something to search...
Next.js catchError

Next.js catchError

error.js catches errors for an entire route segment — but not every error boundary you need actually lines up with a route segment boundary. Sometimes you want error recovery scoped to one specific widget, one card in a dashboard, one section of a page that has nothing to do with how your file system is organized into routes. catchError is Next.js's answer: a programmatic function that builds a proper error boundary — with the framework's own recovery semantics baked in — around any part of your component tree, independent of route structure entirely.

This is a genuinely new, recently-stabilized API (stable as of Next.js 16.3.0), and it's worth understanding both what it gives you over a hand-rolled React error boundary and exactly how it composes with error.js rather than replacing it.

Why Not Just Write a Custom React Error Boundary?

You certainly could — error boundaries are a standard React pattern with getDerivedStateFromError and componentDidCatch. But catchError gives you three things a hand-rolled boundary doesn't provide automatically:

  • Built-in error recovery. Its retry() re-renders the page inside a React Transition, specifically preserving Client Component state that lives outside the error boundary — a hand-rolled boundary's naive re-render wouldn't give you that preservation for free.
  • Framework-aware integration. Next.js APIs like redirect() and notFound() work by throwing special, framework-recognized errors internally. A generic error boundary would catch these the same as any other thrown error — which is exactly wrong, since a notFound() call is supposed to trigger routing behavior, not get swallowed as a generic error. catchError already knows to let these pass through correctly.
  • Client navigation handling. The error state automatically clears the moment the user navigates client-side to a different route — you don't have to manually reset boundary state on route change yourself.

Basic Usage

"use client";
import { catchError, type ErrorInfo } from "next/error";

function ErrorFallback(props: { title: string }, { error, retry }: ErrorInfo) {
  return (
    <div>
      <h2>{props.title}</h2>
      <p>{error.message}</p>
      <button onClick={() => retry()}>Try again</button>
    </div>
  );
}

export default catchError(ErrorFallback);

catchError can be called from Client Components — and the fallback function you pass it must itself be a Client Component (or defined in a 'use client' module), the same requirement error.js has for the same underlying reason: error boundaries are a client-only React feature.

The fallback Function's Two Arguments

const ErrorWrapper = catchError(fallback);

fallback receives:

  • props — whatever props are passed to the wrapper component you get back from catchError, excluding children.
  • errorInfo — an object with three fields:
PropertyTypeDescription
errorErrorThe caught error instance
retry() => voidRe-fetches and re-renders the boundary's children; replaces the fallback with the successful result if it works
reset() => voidResets the error state and re-renders without re-fetching

Exactly the same guidance as error.js applies here: use retry() in most cases, since reset() only clears state without re-running the failed work — meaning it won't recover from a Server Component error, only from certain client-side error states where nothing actually needs re-fetching.

What You Get Back

catchError returns a React component that accepts the same props your fallback's first argument expects, plus children — it wraps children in an error boundary and renders fallback if anything inside throws.

Basic Wiring

import ErrorWrapper from "../custom-error-boundary";

export default function Component({ children }: { children: React.ReactNode }) {
  return <ErrorWrapper title="Dashboard Error">{children}</ErrorWrapper>;
}

The title prop here flows straight through to ErrorFallback's props argument — this is exactly what makes the pattern reusable: build one error-boundary component, wrap it around many different sections of your app, and vary its displayed content per usage through ordinary props.

Server-Rendered Fallback Content

A more advanced pattern: pass genuinely server-rendered content as a React.ReactNode prop, so the fallback UI itself can reflect live, fetched data rather than a static message:

"use client";
import { catchError, type ErrorInfo } from "next/error";

function ErrorFallback(
  props: { fallback: React.ReactNode },
  errorInfo: ErrorInfo,
) {
  return props.fallback;
}

export default catchError(ErrorFallback);
import ErrorBoundary from "../error-boundary";

async function ErrorFallback() {
  const data = await getData();
  return <div>{data.message}</div>;
}

export default function Component({ children }: { children: React.ReactNode }) {
  return <ErrorBoundary fallback={<ErrorFallback />}>{children}</ErrorBoundary>;
}

Worth being deliberate about before reaching for this: the docs explicitly flag that this pattern eagerly renders the fallback content on every single page render, whether or not an error actually occurs — since the fallback prop's JSX has to be constructed regardless of whether the boundary ever triggers it. For most use cases, a simpler client-side-only fallback (the basic pattern above) is genuinely sufficient, and this data-driven fallback pattern is worth reserving for cases where the error UI specifically needs to reflect real, current data rather than a generic message.

catchError vs. error.js — Where Each One Fits

error.jscatchError
ScopeAn entire route segmentAny arbitrary part of the component tree
PlacementFixed file-system location per segmentWrapped around whatever component you choose, wherever it lives
Built-in Next.js error boundaryYes — automaticallyNo — you build the wrapper yourself, once, and reuse it

Critically: you do not need to wrap an error.js default export in catchError. error.js already renders inside a built-in error boundary Next.js provides automatically at the file-convention level — layering catchError on top of it would be redundant, not additive. Reach for catchError specifically for component-level error recovery that doesn't map cleanly onto a route segment boundary — a single dashboard widget, one card in a grid of independently-fetched cards — where error.js's route-segment granularity is simply too coarse for what you actually need.

Version History

VersionChanges
v16.3.0catchError became stable
v16.2.0unstable_catchError introduced

Key Takeaways

AspectDetail
PurposeProgrammatic, component-level error boundaries — independent of route-segment structure
Framework awarenessCorrectly passes through redirect()/notFound()'s internal special errors instead of catching them as generic errors
retry() vs reset()Prefer retry() — it genuinely re-fetches; reset() only clears state
ReusabilityProps flow through to the fallback, making one wrapper reusable across many differently-titled/configured sections
Server-rendered fallback contentPowerful, but eagerly rendered on every page render — reserve for cases that genuinely need it
Relationship to error.jsComplementary, not redundant — don't wrap error.js's own export in catchError

catchError fills a real gap error.js structurally can't: error recovery that doesn't respect route boundaries. Reach for it when a single component — not an entire segment — is the right unit of failure isolation, and lean on its built-in retry() semantics rather than reinventing recovery logic a hand-rolled error boundary would otherwise leave you to build yourself.

Tags :
Share :

Related Posts

Can Next.js Be Used with GraphQL?

Can Next.js Be Used with GraphQL?

Next.js and GraphQL are two powerful technologies that have gained significant traction in the web development community. Next.js, a React-based fram

Dive Deeper
How does Next.js differ from Create React App?

How does Next.js differ from Create React App?

In the world of modern web development, React.js has emerged as a dominant force due to its flexibility, performance, and extensive ecosystem. Two po

Dive Deeper
How does Next.js handle image optimization?

How does Next.js handle image optimization?

In modern web development, image optimization plays a critical role in enhancing user experience and improving site performance. Large, unoptimized i

Dive Deeper