動的ルーティング。角括弧の3つの形
動的ルーティングは、フォルダ名を角括弧で囲むとその部分が変数になる仕組みで、1つぶんを受ける形、1つ以上を受ける形、0個でもよい形の3つがあります。
角括弧は3つの形がある
フォルダ名を角括弧で囲むと、その部分が変数になります。囲み方で受けられる範囲が変わります。
| 書き方 | 受ける数 | そのフォルダ自身 |
|---|---|---|
| [id] | ちょうど1つ | 当たらない |
| [...slug] | 1つ以上 | 当たらない |
| [[...slug]] | 0個でもよい | 当たる |
3行目だけが特別です。app/help/[[...slug]]/page.tsx は /help そのものにも当たります。実機で確かめたビルドの出力です。
├ /docs/[...slug]
│ ├ ● /docs/a
│ └ ● /docs/a/b ← /docs 自体は無い
└ /help/[[...slug]]
├ ● /help ← ここが出る
└ ● /help/x
名前は URL に出ない
角括弧の中の名前は、受け取るときの変数名でしかありません。URL のどこにも現れません。
だから、同じ深さに違う名前で2つ置くと止まります。実機の文言です。
Ambiguous route pattern "/blog/[*]" matches multiple routes:
- /blog/[id]
- /blog/[slug]
/blog/abc が来たとき、それが id なのか slug なのかを決める手がかりが URL にありません。
app/blog/[slug]/page.tsx app/blog/[id]/page.tsx
どちらも /blog の次の1つに当たる。
app/blog/[slug]/page.tsx app/blog/edit/[id]/page.tsx
edit という固定の部分が、当たり先を決めてくれる。
深さが違えば共存できます。/blog/[slug] と /blog/[id]/edit は通ります。止まるのは同じ深さのときだけです。
params は await して受け取る
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <p>{slug}</p>
}
params は Promise です。同期のまま使えた頃のコード例が残っているので、古い記事を写すときは注意してください。await せずに読むと、本番では何も言われないまま undefined になります(params が undefined になる)。
事前に描かれるかどうかは、一覧を渡したかで決まる
動的なパスは、そのままでは毎回サーバーで描く側です。どの値が来るか分からないからです。
実機で確かめました。generateStaticParams を書かないページは、ビルドの成果物にその画面の文字が1件も入りません。
export async function generateStaticParams() {
return [{ slug: 'a' }, { slug: 'b' }]
}
これを書くと、渡した数だけ事前に描かれます。output: 'export' で静的なファイルだけを書き出すときは、この関数が必須です(generateStaticParams が無いエラー)。
├ /blog/[slug]
│ ├ ● /blog/a
│ └ ● /blog/b
└ ƒ /news/[id] ← 渡していないほう
| 中身 | 数 | 渡すか |
|---|---|---|
| 記事・商品・ドキュメント | 決まっている | 渡す |
| 検索結果・絞り込み | 決まらない | 渡さない |
| 件数が非常に多いもの | 多い | よく見られるぶんだけ渡す |
渡していないパスへのアクセスも、既定では 404 になりません。その場で作られて返り、次からは出来あがったものが使われます。この挙動はキャッシュの考え方と地続きです。
無いものは、無いと言う
一覧を渡す渡さないに関係なく、そのデータが存在しないときの扱いは自分で書きます。
const { id } = await params
const item = await findItem(id)
if (!item) notFound()
書かないと、名前も本文も空のページが 200 で返り、クローラにも拾われます。実際に動いているものが404 とエラーの最小構成にあります。
まとめ
- 角括弧は1つぶん / 1つ以上 / 0個でもよいの3つ。そのフォルダ自身に当たるのは3つ目だけ
- 角括弧の中の名前はURL に出ない。同じ深さに2つ置くと止まる
paramsは await して受け取る- 一覧を渡した数だけ事前に描かれる。渡さなければ毎回描く
- 無いときは
notFound()。書かないと空のページが 200 で返る
出典
- nextjs.org/docs/app/api-reference/file-conventions/dynamic-routes
- nextjs.org/docs/app/api-reference/functions/generate-static-params
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。