Next.js Curriculum
MediumFile-System Conventions100% Free20 min read

Not-Found Handling: not-found.tsx and notFound()

The 30-Second Interview Answer

not-found.tsx renders a custom 404 UI for unmatched URLs or when programmatically triggered via notFound(). When invoked during server rendering, Next.js emits an HTTP 404 status code header to search engine crawlers. Like error boundaries, not-found.tsx renders inside the parent layout.

Learning Objectives

Trigger semantic 404 responses programmatically using notFound().

Understand HTTP 404 status code emission during SSR vs client navigation.

Structure segment-level vs global root 404 pages.

Prevent soft 404 SEO penalties by returning proper HTTP response codes.

Prerequisites & Background Knowledge
HTTP status codesApp Router dynamic segments

Why Interviewers Care

High relevance for SEO compliance, crawler indexing, and robust dynamic entity fetching.

The Challenge

How does Next.js handle 404 states with not-found.tsx and the notFound() function? How does it prevent "soft 404" SEO penalties during server rendering?

Code Architecture & Implementation

1// app/products/[id]/page.tsx
2import { notFound } from 'next/navigation';
3
4export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
5 const { id } = await params;
6
7 const product = await fetch(`https://api.store.com/products/${id}`, {
8 cache: 'no-store'
9 }).then(res => (res.ok ? res.json() : null));
10
11 // If item does not exist in DB, trigger notFound()
12 if (!product) {
13 notFound(); // Throws NEXT_NOT_FOUND error internally
14 }
15
16 return (
17 <div>
18 <h1>{product.title}</h1>
19 <p>{product.description}</p>
20 </div>
21 );
22}
23
24// app/products/[id]/not-found.tsx
25// Rendered inside products/[id] layout with HTTP 404 status header
26export default function ProductNotFound() {
27 return (
28 <div className="not-found-card">
29 <h2>Product Does Not Exist</h2>
30 <p>The requested product has been discontinued or removed.</p>
31 </div>
32 );
33}

Architectural Explanation

Mechanics of notFound()

When notFound() is invoked inside a Server Component, Route Handler, or Server Action:

  1. Control Flow Exception: Next.js throws an internal NEXT_NOT_FOUND error that halts component execution.
  2. HTTP 404 Emission: During initial SSR, Next.js sets the HTTP response header to 404 Not Found. This is critical for SEO: it prevents "soft 404s" (where missing pages return 200 OK, wasting search crawler budgets).
  3. UI Replacement: The nearest parent not-found.tsx file is rendered in place of the page.
  4. Layout Scoping: The surrounding layout (headers, footers) remains intact.

Segment-Level vs Global 404

  • app/not-found.tsx: Handles globally unmatched URLs (e.g. /unknown-path).
  • app/products/[id]/not-found.tsx: Handles missing dynamic entities specifically within that route segment.

Common Mistakes & Anti-Patterns

Wrapping notFound() in a generic catch (error) block without re-throwing, preventing Next.js from rendering the 404 UI.

Returning a custom <p>Not found</p> component without calling notFound(), causing a soft 404 with a 200 OK HTTP status code.

Expecting not-found.tsx to receive route parameters as props (it receives no props).

Interview Follow-Up Traps

Interviewer asks: "If a user navigates on the client via <Link> to a page that calls notFound(), does the browser make a new HTTP request?" Trap answer: Yes, to get the 404 status. Correct answer: No. During client navigation, Next.js receives the RSC payload indicating not-found and swaps the UI via client-side routing.

Interviewer asks: "What happens if you wrap notFound() in a try/catch (err) block?" Trap answer: It safely logs the 404. Correct answer: The catch block swallows the NEXT_NOT_FOUND exception and breaks the 404 redirection.

Frequently Asked Questions

What is a soft 404 and why is it harmful for SEO?

A soft 404 occurs when a page displays "Page Not Found" to users but returns a 200 OK HTTP status code to search engines, causing indexing confusion and wasted crawl budget.

Yes. not-found.tsx can be async and fetch recommendations or search links.

Yes, calling notFound() in a Route Handler returns an HTTP 404 response.

No. not-found.tsx receives no props. To inspect params on the client, use the usePathname() hook.

Create app/not-found.tsx at the root of the app directory.