R
Rishtaara
Next.js: Zero to Full-Stack
Lesson 27 of 42Article14 min

Handling Errors with error.tsx & notFound

If a Server Component throws, the nearest error.tsx shows a recovery UI. It must be a Client Component so the reset button can re-render the segment.

error.tsx — catch unexpected failures

If a Server Component throws, the nearest error.tsx shows a recovery UI. It must be a Client Component so the reset button can re-render the segment.

Real-life example: error.tsx is the fire exit sign — when the kitchen catches fire (crash), guests get a clear path instead of a blank wall (white screen).

app/dashboard/error.tsx
"use client";

export default function DashboardError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <main role="alert">
      <h2>Something went wrong on the dashboard</h2>
      <p>{error.message}</p>
      <button type="button" onClick={() => reset()}>
        Try again
      </button>
    </main>
  );
}

notFound() — missing resources

When an id or slug does not exist, call notFound(). Next.js renders the closest not-found.tsx (or the default 404).

Use notFound for expected missing data. Use throw / error.tsx for unexpected failures (DB down, bug, 500). Custom global UI lives in app/global-error.tsx for root layout crashes.
Triggering and customizing 404
import { notFound } from "next/navigation";

export default async function InvoicePage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const res = await fetch(`https://api.example.com/invoices/${id}`, {
    cache: "no-store",
  });

  if (res.status === 404) notFound();
  if (!res.ok) throw new Error("Failed to load invoice");

  const invoice = await res.json();
  return <h1>Invoice #{invoice.id}</h1>;
}

// app/invoices/[id]/not-found.tsx
export default function InvoiceNotFound() {
  return (
    <main>
      <h2>Invoice not found</h2>
      <p>Check the link or go back to the invoice list.</p>
    </main>
  );
}