TL;DR
このページでは、Next.js のダイナミックルーティングの実装方法について解説します。事前にセグメント名を知らなくても、動的なデータからルートを作成することが可能になります。これにより、ブログ記事のような動的なコンテンツを効率的に管理することができます。
この記事は、Next.js の公式ドキュメンテーションを参考について解説しています。特に、Dynamic Routesのセクションを基に、その内容を理解しやすく解釈し直し、さらに詳細な説明を加えています。
この記事は、Next.js の公式ドキュメンテーションの素晴らしい内容を尊重し、その知識を広めることを目指しています。そのため、この記事の内容は、公式ドキュメンテーションの内容をそのまま意訳するのではなく、それを基に私たち自身の理解と経験を元にした解釈と説明を加えています。
もし、より詳細な情報や公式の説明を求める場合は、Next.js の公式ドキュメンテーションを直接ご覧いただくことを強く推奨します。この記事の解説はあくまで参考の一つであり、公式ドキュメンテーションには、より詳細な情報や最新の更新情報が含まれています。
| 開発環境 | バージョン |
|---|---|
| Next.js | 13.4.4(Pages Router) |
| TypeScript | 5.0.4 |
| Emotion | 11.11.0 |
| React | 18.2.0 |
セグメントとは?
セグメントというのは、道路の一部分のようなものです。
例えば、あなたが学校に行くとき、家から学校までの道のりを考えてみましょう。まず家を出て、最初の交差点まで行きます。次に、その交差点から公園まで行きます。そして、公園から学校まで行きます。このとき、家から交差点までの道のり、交差点から公園までの道のり、公園から学校までの道のり、それぞれが「セグメント」です。
ウェブサイトの URL も同じように考えることができます。例えば、https://example.com/shop/clothes/tops という URL があるとします。この URL は、https://example.com/ から始まり、そこから shop/、次に clothes/、最後に tops と進んでいきます。このとき、shop/、clothes/、tops それぞれが URL の「セグメント」です。
つまり、セグメントとは、道のりや URL をいくつかの部分に分けたときの一部分のことを指します。
ダイナミックルーティング
事前に正確なセグメント名を知らない場合や、動的なデータからルートを作成したい場合、ダイナミックセグメントを使用すると便利です。これはリクエスト時に埋め込まれるか、ビルド時に事前にレンダリングされます。
ダイナミックセグメントのルール
ダイナミックセグメントは、フォルダ名を角括弧で囲むことで作成できます。例えば、[id] や [slug] などです。ダイナミックセグメントは useRouter を使ってアクセスできます。
ダイナミックセグメントの名前は任意に設定することができます。ただし、その名前は URL の一部となり、またコード内でその値にアクセスするためのキーとなるため、そのページの内容を適切に表す名前を選ぶことが推奨されます。
例えば、ブログ記事のページを作成する場合、記事の一意の識別子を表すために [slug] や [id] といった名前がよく使われます。また、ユーザープロフィールページを作成する場合、ユーザー名を表すために [username] といった名前を使うこともあります。
このように、ダイナミックセグメントの名前はそのページの内容や目的に応じて選ぶことができます。
ルートのディレクトリマップ
今回解説するルートのディレクトリマップは次のようになります。
pages/
├─ blog/
│ ├─ [slug].js
├─ shop/
│ ├─ [...slug].js
│ ├─ [[...slug]].jsこれは、ウェブサイトの「地図」のようなものです。それぞれのフォルダやファイルが、ウェブサイトの特定の部分を表しています。
-
pages/:これはすべてのページが入っている大きな箱のようなものです。ウェブサイトのすべてのページは、この中に入っています。 -
blog/:これはブログのページが入っているフォルダです。ブログに関連するすべてのページは、この中に入っています。 -
[slug].js:これはブログの各記事を表すファイルです。[slug]の部分は、各記事の URL の一部になります。例えば、/blog/my-first-postのような URL になります。 -
shop/:これはショップのページが入っているフォルダです。ショップに関連するすべてのページは、この中に入っています。 -
[...slug].jsと[[...slug]].js:これらは商品の各ページを表すファイルです。[...slug]と[[...slug]]の部分は、各商品の URL の一部になります。例えば、/shop/clothes/tops/t-shirtsのような URL になります。
このように、フォルダとファイルを組み合わせることで、ウェブサイトの各ページの URL を作成します。そして、[...] や [[...]] のような特殊な記号を使うことで、動的な URL を作成することができます。これが、Next.js のダイナミックルーティングの仕組みです。
例
例えば、ブログでは次のようなルート pages/blog/[slug].js を含めることができます。ここで、[slug] はブログ記事のダイナミックセグメントです。
// 'next/router'からuseRouterをインポートします
import { useRouter } from 'next/router'
// Pageという関数コンポーネントをエクスポートします
export default function Page() {
// useRouterを使ってrouterオブジェクトを取得します
const router = useRouter()
// router.query.slugを使って、動的なslugを取得し表示します
return <p>Post: {router.query.slug}</p>
}
このコードは、URL の動的な部分(この場合は 'slug')を取得し、それを表示します。例えば、URL が '/blog/a' の場合、画面には 'Post: a' と表示されます。
ブログのディレクトリマップ
一般的なブログを作る場合、ディレクトリ構造は次のようになります。
.
├── api
│ ├── feed.ts
│ └── og.tsx
├── page
│ └── [page].tsx
├── tags
│ ├── [tags]
│ │ └── page
│ │ └── [page].tsx
│ ├── [tags].tsx
│ └── index.tsx
├── _app.tsx
├── _document.tsx
├── [id].tsx
├── index.tsx
└── search.tsx-
api/: このフォルダは、API ルートを格納します。Next.js では、このフォルダ内のファイルは API エンドポイントとして扱われます。feed.tsとog.tsxは、それぞれ異なる API エンドポイントを表します。 -
page/: このフォルダは、ページネーションされたページを格納します。[page].tsxは、ページ番号に基づいて異なるページを表示します。 -
tags/: このフォルダは、タグに関連するページを格納します。[tags].tsxは、特定のタグに関連するページを表示します。[tags]/page/[page].tsxは、特定のタグに関連するページネーションされたページを表示します。index.tsxは、すべてのタグを一覧表示するページを表します。 -
_app.tsx: このファイルは、Next.js アプリケーションのメインコンポーネントを定義します。すべてのページはこのコンポーネントを通じてレンダリングされます。 -
_document.tsx: このファイルは、サーバーサイドでのみレンダリングされ、HTML ドキュメントの初期構造を定義します。 -
[id].tsx: このファイルは、特定の ID を持つアイテムのページを表示します。ID は URL の一部として提供されます。 -
index.tsx: このファイルは、ウェブサイトのホームページを表示します。 -
search.tsx: このファイルは、検索結果のページを表示します。
このように、各フォルダとファイルはウェブサイトの特定の部分を表し、それぞれが特定のタスクを担当しています。
キャッチオールセグメント
ダイナミックセグメントは、ブラケット内に省略記号(...)を追加することで、その後のすべてのセグメントをキャッチするように拡張することができます。
例えば、pages/shop/[...slug].js は /shop/clothes にマッチしますが、さらに /shop/clothes/tops、/shop/clothes/tops/t-shirts などにもマッチします。
以下のコードは、pages/shop/[...slug].tsx という名前のファイルに保存します。このファイル名は、Next.js のダイナミックルーティングの規則に従っています。[...slug] の部分が URL の動的な部分を表しています。
pages/shop/[...slug].tsx
// 'next/router'からuseRouterをインポートします
import { useRouter } from 'next/router'
import { NextPage } from 'next'
// Pageという関数コンポーネントをエクスポートします
const Page: NextPage = () => {
// useRouterを使ってrouterオブジェクトを取得します
const router = useRouter()
// router.query.slugを使って、動的なslugを取得し表示します
// slugは配列なので、joinメソッドを使って文字列に変換します
return <p>Shop: {router.query.slug?.join('/')}</p>
}
export default Page
このコードは、URL の動的な部分(この場合は 'slug')を取得し、それを表示します。例えば、URL が '/shop/clothes/tops' の場合、画面には 'Shop: clothes/tops' と表示されます。
オプションのキャッチオールセグメント
キャッチオールセグメントは、パラメータを二重の角括弧で囲むことでオプションにすることができます:[[...folderName]]。
例えば、pages/shop/[[...slug]].js は /shop にもマッチします。さらに /shop/clothes、/shop/clothes/tops、/shop/clothes/tops/t-shirts などにもマッチします。
キャッチオールセグメントとオプションのキャッチオールセグメントの違いは、オプションの場合、パラメータなしのルートもマッチすることです(上記の例では /shop)。