本文へ移動
verified onnext@16.3.5react@19
最終検証日 2026-09-26
Next.js道場

Next.js の一覧と詳細ページ。最小構成サンプル

一覧から詳細へ辿る形は、ファイル4つで足ります。

動いているものを触る/demo/list

ファイル構成

並んでいるのは、上の画面をいま動かしている本物のファイルです。名前を押すと中身が出ます。

ファイル構成
  • app/
  • [id]/
app/layout.tsxBuild

全ページの外側。本物のプロジェクトでは html と body をここで書きます

import type { Metadata } from "next";
 
export const metadata: Metadata = {
  title: "一覧と詳細の最小構成",
};
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return <main>{children}</main>;
}
.tsx
画面を返すファイル。HTML のような書き方ができます
.ts
画面を返さないファイル。処理とデータだけを置きます。ここに画面を書くと原因を名指ししないエラーで落ちます
Build
ビルド時に一度だけ動きます
Server
アクセスのたびにサーバーで動きます
Browser
利用者の手元で動きます

角括弧がアドレスの可変部分になる

詳細ページを作るのに、ルーターの設定は要りません。フォルダ名を角括弧で囲むだけです。

app/page.tsx        →  /demo/list
app/[id]/page.tsx   →  /demo/list/boundary
                       /demo/list/cache
                       /demo/list/form

囲んだ名前がそのまま受け取れます。

export default async function Detail({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params

ビルド時に作っておく

generateStaticParams は、「どの id でページを作っておくか」をビルドに教える関数です。

export async function generateStaticParams() {
  const books = await listBooks()
  return books.map((b) => ({ id: b.id }))
}

実機で確かめました。ビルドの一覧に3件ぶんが並びます。

├   /demo/list/[id]
│ ├ ● /demo/list/boundary
│ ├ ● /demo/list/cache
│ └ ● /demo/list/form

● は「ビルド時に作った」という印です。リクエストのたびに作り直しません。

詳細ページができるまで
  1. Build
    id の一覧を作る

    generateStaticParams がデータを読んで、作るべきアドレスを返す。

  2. Build
    その数だけページを作る

    3件なら3枚。ここまでで終わっている。

  3. Browser
    できたものを配る

    アクセス時にサーバーは何も作らない。

書かなければどうなるか、というと落ちはしません。リクエストのたびに作られるようになるだけです。件数が読めているなら、書いておくほうが速く出せます。

知らない id で来たとき

const book = await findBook(id)
if (!book) notFound()

実機で確かめました。/demo/list/nonexistent は 404 を返します。

データはブラウザに届かない

app/data.ts を読んでいるのはサーバー側だけです。だからこの配列は、ブラウザには届きません。

一覧を開いたとき、どちらに何が残るか
ブラウザに届く
画面に出した題名
詳細へのリンク
サーバーに残る
配列そのもの
著者と本文
データを読む処理

実機で確かめました。ビルドしたものを検査して、著者名も本文も、配信される JavaScript には1件も含まれていませんでした。届いているのは、一覧に表示した題名だけです。

件数が3件でも3万件でも同じです。絞ってから送るか、送ってから絞るかの違いが、そのまま通信量の違いになります。

ここから足していくとき

  • ページ送り。searchParams でページ番号を受け取ります。そこを読んだ時点で一覧は動的になります
  • 絞り込み。検索の最小構成にあります。そこで初めて Client Component が1つ要ります
  • 更新。書き込む口を作るなら Server Function の呼び方の形になります

ここまでの3つのサンプルには、Client Component が1つもありません。次でようやく1つ出てきます。

入れていないもの

このまま本番に持っていかないでください。省いてあるのは次の点です。

  • データベース。3件の配列を返しているだけです
  • ページ送り。件数が増えたら一覧を区切る必要があります
  • 並び替えと絞り込み。それは検索の最小構成で扱っています
  • 更新。読むだけで、書き込む口はありません

出典

このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。

理解できたか試す

では、このコードはどうなりますか。

詳細ページを事前に作りたくて generateStaticParams を書きました。ビルドするとどうなりますか。

この問題を解くこのテーマの問題 3 問