Next.js

Verified against Claude Code · 2026-07-29

Design an error.tsx hierarchy that fails at the right scope instead of taking down the whole page

Places error.tsx boundaries at the segment that actually owns each failure mode, distinguishes handled not-found cases from real crashes, and scopes global-error.tsx to what it can actually catch.

Claude CodeCursorClaude3 fillable variables

The prompt

Ready to copy — highlighted parts are example details you can swap.

You are designing the error-handling hierarchy for a route tree, deciding where error.tsx boundaries belong and what global-error.tsx is actually responsible for, so a failure in one section degrades only that section instead of crashing the whole page.

ROUTE TREE
app/layout.tsx (root) wraps app/dashboard/layout.tsx, which wraps app/dashboard/projects/[id]/page.tsx and app/dashboard/billing/page.tsx

FAILURE MODES TO HANDLE
A project id in the URL that doesn't exist or the user can't access; the billing page's payment-provider API occasionally timing out; a genuinely unexpected exception anywhere in the dashboard layout's own data fetch

CURRENT ERROR HANDLING
There's one error.tsx at app/dashboard/layout.tsx today — a billing API timeout currently crashes the entire dashboard, including the unrelated projects list

DESIGN RULES
Place an error.tsx at the route segment that actually owns the operation most likely to fail — a data fetch, a mutation — rather than only at the top of the tree, since an error.tsx catches errors thrown by anything beneath it in the same segment, including its own children, but a boundary placed too high catches the error just as correctly while taking a much larger, unrelated portion of the page down with it as collateral. Every error.tsx file must be a Client Component — it needs 'use client' at the top, since it receives an error object and a reset function as props and needs interactivity, specifically a retry button that calls reset(), to be useful rather than just a static message. Distinguish an expected, handled absence from a genuine unexpected error: calling notFound() for a resource that legitimately doesn't exist should render the segment's not-found.tsx, a deliberate, calm UI state, not error.tsx, which is meant for something that actually went wrong and wasn't supposed to happen; conflating the two means a normal, everyday case like a bad URL slug renders through the same alarming error UI as a genuine crash. Reserve global-error.tsx, which must live directly in the app directory and also be a Client Component, for errors thrown by the root layout itself — it's the only boundary that can catch a failure in the root layout, precisely because every other error.tsx in the tree is rendered as a child of that same root layout and therefore can't catch an error the layout itself throws before any child ever mounts. Log every caught error with enough context to actually debug it later — the route segment, the error's own message and digest if one exists, and any relevant identifiers from the request, such as which resource was being fetched — rather than only rendering a generic apology to the user with nothing captured server-side for whoever has to investigate the failure afterward.

OUTPUT FORMAT
A tree diagram showing where each error.tsx (and any not-found.tsx) sits, paired with what specifically triggers each one and what its fallback UI does — including whether it offers a retry via reset(). Close with one line confirming what global-error.tsx here is actually scoped to catch, and confirming it is not being relied on as a catch-all for errors that already have a more specific boundary lower in the tree.

Customize

Optional — swap in your own details for the highlighted parts above.

Why this works

An error.tsx boundary catches any error thrown by its own segment and everything nested beneath it, which is exactly why placement is the whole decision here — a single boundary at a shared layout technically satisfies "there is error handling" while still taking down every unrelated sibling segment the moment any one of them throws, since the boundary has no way to know which specific child actually failed once it's caught the error at that scope; moving the boundary down to the segment that owns the actual risky operation is what turns "the dashboard crashed" into "the billing panel shows a retry button while the projects list keeps working normally." The distinction between notFound() and a thrown error reflects a real, separate rendering path Next.js provides on purpose: notFound() is a deliberate signal meaning this specific, expected case has no content, and it renders not-found.tsx, a calm, designed-for state, while error.tsx is reserved for something that wasn't supposed to happen at all — collapsing the two into one error.tsx means a visitor who mistyped a URL sees the same alarming "something went wrong, try again" UI as a visitor who hit an actual server crash, which is both a worse experience for the common case and a false alarm for whoever monitors error rates if not-found hits get logged as errors. The global-error.tsx scoping rule is grounded in a structural fact about how the tree nests, not a convention: every other error.tsx in the app is itself rendered as a descendant of the root layout, so none of them can catch an error the root layout throws before any child has even mounted — global-error.tsx, sitting outside and above that layout, is the only boundary physically positioned to catch that one specific class of failure, and treating it as a general catch-all for errors that already have a more specific boundary elsewhere just means two boundaries are now theoretically eligible to catch the same error with no clear reason to prefer one's fallback UI over the other's.

What you get back

app/dashboard/billing/error.tsx — catches a payment-provider timeout or any other billing-fetch failure; shows "Couldn't load billing right now" with a retry button wired to reset(). app/dashboard/projects/[id]/not-found.tsx — handles a project id that doesn't exist or isn't accessible via notFound(), showing "Project not found" with a link back to the project list, not an error state. app/global-error.tsx — scoped only to a failure in app/layout.tsx itself, such as a broken root-level data fetch; it is not relied on to catch the billing timeout, which already has its own, more specific boundary two levels down.

Verified against

Claude Code Sonnet 4.6 · 2026-07-29

Claude Sonnet 4.6 · 2026-08-05

Changelog

  • 2026-07-29 — Initial publish, verified against Claude Code and Claude (Sonnet 4.6) on a Next.js 16 dashboard with a shared parent layout and independent child segments.

Need this built into your business?

If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.

EXPLORE CUSTOM SOFTWARE
All Next.js prompts

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY