Next13のURLパスの決まり方

Posted: 2023/2/23
Next.js/
Next13

今までの 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.tsxparamsは、

{ 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"}