概要
notFound() 関数と not-found.tsx を組み合わせてカスタム 404 ページを実装する。動的ルートでリソースが存在しない場合に notFound() を呼び出すことで、not-found.tsx に制御を渡す。error.tsx(ランタイムエラー用)とは責務が異なる。
インストール
# 追加インストールは不要
実装
カスタム 404 ページ
// app/not-found.tsx
import Link from "next/link";
export default function NotFound() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-8 text-center">
<h1 className="mb-2 text-6xl font-bold text-gray-200">404</h1>
<h2 className="mb-4 text-2xl font-semibold text-gray-800">
ページが見つかりません
</h2>
<p className="mb-8 text-gray-500">
お探しのページは存在しないか、削除された可能性があります。
</p>
<Link
href="/"
className="rounded bg-blue-600 px-6 py-2 text-sm text-white hover:bg-blue-700"
>
トップページへ戻る
</Link>
</main>
);
}
動的ルートでの notFound() 呼び出し
// app/posts/[id]/page.tsx
import { notFound } from "next/navigation";
type Post = { id: number; title: string; body: string };
async function getPost(id: number): Promise<Post | null> {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${id}`,
{ next: { revalidate: 60 } }
);
if (!res.ok) return null;
return res.json();
}
type Props = {
params: Promise<{ id: string }>;
};
export default async function PostPage({ params }: Props) {
const { id } = await params;
const post = await getPost(Number(id));
// post が取得できない場合は not-found.tsx に制御を渡す
if (!post) notFound();
return (
<article className="mx-auto max-w-2xl p-8">
<h1 className="mb-4 text-2xl font-bold">{post.title}</h1>
<p className="text-gray-600">{post.body}</p>
</article>
);
}
セグメント固有の not-found.tsx(任意)
セグメント配下専用の 404 画面を設ける場合は、ルートと同じディレクトリに配置する。
// app/posts/[id]/not-found.tsx
import Link from "next/link";
export default function PostNotFound() {
return (
<main className="mx-auto max-w-xl p-8 text-center">
<h2 className="mb-4 text-xl font-semibold text-gray-800">
この記事は存在しません
</h2>
<Link href="/posts" className="text-blue-600 hover:underline">
記事一覧へ戻る
</Link>
</main>
);
}
ポイント
notFound()はnext/navigationからインポートし、Server Component 内で呼び出す。呼び出し後のコードは実行されないapp/not-found.tsxはアプリ全体のフォールバック 404 ページ。セグメント直下に置くと、そのルート専用の 404 ページになるerror.tsxはランタイムエラー(throw されたエラー)用、not-found.tsxは意図的な 404 用として使い分ける- Next.js 15 では
paramsがPromise型のためawait paramsが必要 generateStaticParamsを使った静的生成ルートでは、存在しない ID にアクセスすると自動的に 404 になるが、明示的にnotFound()を呼ぶことで動的レンダリング時も同じ挙動を保証できる