Dev Study
← 解説「not-found.tsx — 404ページを作る」に戻る

サンプルコードで身につける: not-found.tsx — 404ページを作る

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1最小の not-found.tsx

404 ページの最小形です。app/not-found.tsx に default export を置くだけで、どのルートにもマッチしない URL へのアクセス時に自動で表示されます。

// app/not-found.tsx — マッチしない URL で自動表示
export default function NotFound() {
  return <h1>ページが見つかりません</h1>;
}

// loading.tsx と同じく、置くだけで有効になる。
// 存在しない /xyz などにアクセスすると表示される。

2帰り道つきの404ページ

行き止まりで終わらせず、トップページや主要ページへの導線を置いたデザイン済み 404 の例です。実務の 404 ページの役割は「謝ること」より「次の行き先を示すこと」で、Link との組み合わせが定番です。

// app/not-found.tsx
import Link from "next/link";

export default function NotFound() {
  return (
    <main>
      <h1>404 — お探しのページが見つかりません</h1>
      <p>URL が変更されたか、ページが削除された可能性があります。</p>
      <ul>
        <li>
          <Link href="/">トップページへ</Link>
        </li>
        <li>
          <Link href="/products">商品一覧へ</Link>
        </li>
      </ul>
    </main>
  );
}

3notFound() でデータなしを404にする

URL の形は正しいがデータが存在しない場合に、notFound() を呼んで 404 画面へ切り替える例です。動的ルートとの組み合わせで必ず出てくるパターンで、「変な ID を入れたら空白ページ」という事故を防ぎます。

// app/blog/[id]/page.tsx
import { notFound } from "next/navigation";

const posts = [
  { id: "1", title: "はじめての投稿" },
  { id: "2", title: "Next.js 入門" },
];

export default async function PostPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const post = posts.find((p) => p.id === id);
  if (!post) {
    notFound(); // ここで処理が止まり、404 画面に切り替わる
  }
  return <h1>{post.title}</h1>;
}

4「見つからなければ404」の分岐を再現する

notFound() の背後にある「データを探す → なければ 404、あれば 200 で本文」という分岐を、プレーンな TypeScript で実行する例です。ステータスコードと表示内容がワンセットで決まる、という Web の基本構造が見えてきます。

TypeScript

5ECサイトの商品用404

売り切れや販売終了で商品ページがなくなる EC サイトでは、404 が日常的に発生します。商品が見つからないときは検索やカテゴリ一覧へ誘導する専用の案内を返す、離脱を防ぐための実務的な構成です。

// app/products/[id]/page.tsx — 商品がなければ notFound()
import { notFound } from "next/navigation";
import Link from "next/link";

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const product = findProduct(id); // 在庫データから探す(別途定義)
  if (!product) notFound();
  return <h1>{product.title}</h1>;
}

// app/not-found.tsx 側では、行き止まりにしない案内を返す
// <h1>商品が見つかりません</h1>
// <p>販売終了の可能性があります。</p>
// <Link href="/products">商品一覧から探す</Link>

declare function findProduct(id: string): { title: string } | null;