今までの Next.js ではpages
ディレクトリのファイル・ディレクトリがそのまま URL に反映、例えばpage/sample.tsx
というファイルを作成した場合は、/sample
にアクセスすればそのページを見ることができました。
一方 Next13 からのapp
ディレクトリ構成では、そのルーティング方法が変わっていますので、どのように変更があったか見ていきましょう。
※ 前回の記事を見てもらうことを推奨します
URL パスの決まり方
URL パス (https://tyotto-good.com/nextjs) のようなドメインに続くパスは、app
ディレクトリの配下にあるディレクトリによって決まります。
例えば、以下のようなディレクトリ構成を考えます。
app
├── page.tsx
├── layout.tsx
└── shops
└── page.tsx
この時のapp/page.tsx
は URL /
の UI に相当し(ルートパス)、app/shops/page.tsx
は URL /shops
の UI に相当します。
動的なパスの対応
パスが動的に変更する場合、例えば
[https://tyotto-good.com/shops/xxx](https://tyotto-good.com/shops/xxx)
の xxx が定まっていない場合などに対応するには、[]
を用いる必要があります。
以下のようなディレクトリ構成にした場合は、[id]
が動的パスにあたり、[/shops/](https://tyotto-good.com/shops/xxx)foods
や、/shops/fish
など id 箇所が動的な値を入れることができるようになります。
app
└── shops
├── [id]
│ └── page.tsx
動的なパスを用いた場合は、page ファイルの props でparams
というパラメータを受け取ることができ、[/shops/](https://tyotto-good.com/shops/xxx)foods
にアクセスあればshops/[id]/page.tsx
のparams
は、
{ id: 'foods' }
のようなオブジェクトになります。詳細は、page ファイルについてで述べます。
後続の全てのパスに対応する
さらに後続のパスにまで対応したい場合は、[…folderName]
と…
を追加することで後続のパス全てに対応することができます。
以下のようなディレクトリ構成を想定します。
app
└── shops
├── [...id]
│ └── page.tsx
この場合、/shops/foods/rice/1
にアクセスすると、shops 以下全てのパスに対応することができるので、/shops/page.tsx
が処理を引き受けるようになります。
また、この場合のparams
は以下のように値を配列で受け取るようになります。
{
id: ["food", "rice", "1"];
}
注意点としては、こちらの記法を使った場合は/shops
パスに対応しないという点です。
アクセスした場合は、404 判定となります。
こちらに対応したい場合は、[[…folderName]]
を用います。括弧を 2 重にすると一致する範囲が広がり、/shops
にアクセスした時ページが表示されるようになります。
app
└── shops
├── [[...id]]
│ └── page.tsx
app ディレクトリ以下のディレクトリを URL パスに直接対応させたくない場合
URL のパスとは紐付けづに、app ディレクトリ以下のディレクトリをグルーピングしたい場合があると思います。
そのような場合は、ルートグループ
を用いて、ディレクトリ名を()
囲うことで対応できます。
例えば、以下のような構成を考えます。
app
├── (category)
│ └── item
│ └── page.tsx
この場合、(category)
がルートグループになっており、URL に影響を与えることなくグルーピングしていることになります。
よって、/app/item
にアクセスすると、/app/(category)/item/page.tsx
の UI が表示されることになります。
ただこの場合、
app
├── (category)
│ └── item
│ └── page.tsx
├── (sample)
│ └── item
│ └── page.tsx
のような構成にしてしまうとパスがバッティング(/app/item
パスが複数存在)してしまいエラーを引き起こすので注意が必要です。
page ファイルについて
この時のpage.tsx
はパスに紐づく UI を表し、今までのpages
ディレクトリ配下のファイルのような役割を果たします。
受け取る props は先ほどのセクションで説明があったように、以下があります。
props 名 | 説明 |
---|---|
params (optional) | 動的なパスパラメータ |
searchParams (optional) | クエリパラメータ |
どちらも URL に紐づくパラメータを受け取ることがわかります。
では以下のようなディレクトリ構成・page ファイルの場合の挙動を確認してみましょう。
app
└── shops
├── [...id]
│ └── page.tsx
page.tsx
import { NextPage } from "next";
interface PageProps {
params: {
id: string[];
};
searchParams?: { [key: string]: string | string[] | undefined };
}
const ShopPage: NextPage<PageProps> = ({ params, searchParams }) => {
return (
<main>
<div>params: {JSON.stringify(params)}</div>
<div>searchParams: {JSON.stringify(searchParams)}</div>
</main>
);
};
export default ShopPage;
この場合、/shops/food?q=rice
にアクセスがあった場合、以下のような表示になります。
params: {"id":["food"]}
searchParams: {"q":"rice"}