Not Found Pages
Build custom 404 experiences per route segment using not-found.tsx and the notFound() function.
The Default 404
Visiting a URL with no matching route already shows a basic 404 page out of the box. not-found.tsx lets you replace that default with your own design, scoped to any part of your route tree.
not-found.tsx
// app/blog/not-found.tsximport Link from 'next/link';
export default function NotFound() { return ( <div> <h2>Post not found</h2> <p>We couldn\'t find the blog post you\'re looking for.</p> <Link href="/blog">Back to all posts</Link> </div> );}Placing not-found.tsx inside app/blog/ scopes its custom design to anything under /blog — a not-found.tsx at the app root instead becomes the site-wide default.
Triggering It with notFound()
For dynamic routes, you often only know a resource is missing after fetching it. Calling the notFound() function from next/navigation inside a page immediately renders the nearest not-found.tsx and sets a proper 404 status.
import { notFound } from 'next/navigation';
export default async function BlogPostPage({ params }) { const post = await getPostBySlug(params.slug);
if (!post) { notFound(); }
return <article>{/* render post */}</article>;}Both the default 404 and a custom not-found.tsx correctly return a 404 HTTP status code — this matters for SEO, since search engines need to know a page genuinely doesn't exist.
FAQs
No — not-found.tsx handles "this content doesn't exist", while error.tsx handles unexpected failures during rendering. They're separate, purpose-built files.
Yes — a single one at app/not-found.tsx acts as the site-wide fallback for any unmatched route.
Summary
not-found.tsx and notFound() give you full control over how missing content is presented, per section of your app. With routing covered, the next section moves into rendering — starting with Server Components, the default building block of the App Router.