patterntypescriptnextjsModerate
not-found.tsx and notFound() for 404 handling in App Router
Viewed 0 times
Next.js 13+ with App Router
notFoundnot-found.tsx404 pagecustom 404missing resourcenavigation notFound
Problem
In App Router, returning a 404 from a page component or API is not straightforward. There's no res.status(404) — you need to throw a specific Next.js error or use the notFound() function.
Solution
Call notFound() to trigger the nearest not-found.tsx, and create not-found.tsx files at appropriate route levels:
// app/not-found.tsx — global 404 page
export default function NotFound() {
return (
<div>
<h2>404 - Page Not Found</h2>
<a href='/'>Go Home</a>
</div>
);
}
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await fetchPost(slug);
if (!post) {
notFound();
}
return <Article post={post} />;
}
// app/blog/not-found.tsx — segment-specific 404
export default function BlogNotFound() {
return <div>This blog post doesn't exist.</div>;
}
// app/not-found.tsx — global 404 page
export default function NotFound() {
return (
<div>
<h2>404 - Page Not Found</h2>
<a href='/'>Go Home</a>
</div>
);
}
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await fetchPost(slug);
if (!post) {
notFound();
}
return <Article post={post} />;
}
// app/blog/not-found.tsx — segment-specific 404
export default function BlogNotFound() {
return <div>This blog post doesn't exist.</div>;
}
Why
notFound() throws a special Next.js error that triggers the nearest not-found.tsx boundary. Without calling notFound(), a missing resource would render an empty page or throw an unhandled exception, degrading user experience and hurting SEO (200 status for missing content).
Gotchas
- notFound() throws and terminates execution immediately — no need for a return after it
- not-found.tsx at the app root level is also the custom 404 page (replaces pages/404.tsx)
- In Route Handlers, return NextResponse.json({ error: 'Not found' }, { status: 404 }) instead
- notFound() only works in Server Components and Route Handlers, not in Client Components
Code Snippets
Triggering 404 from a Server Component
import { notFound } from 'next/navigation';
const user = await db.user.findUnique({ where: { id } });
if (!user) notFound();Context
When handling missing resources and 404 states in Next.js App Router
Revisions (0)
No revisions yet.