Next.js 13で導入されたgenerateStaticParams()関数は、getStaticPaths()関数の代替である。動的ルートセグメントとセットで使う。

関数の概要

generateStaticParams()関数は、動的ルートセグメントのlayout.tsx、あるいはpage.tsxの中で定義する(tsxがスタンダードだが、jsでもjsxでもtsでもよい)。

これはApp Routerで採用された仕組みである。Pages Routerでは機能しない。

例えば、トップページの下に任意のセグメントをぶら下げたい場合、app/[seg]/page.tsxに以下のように定義する(segは任意の文字列)。

tsx
export async function generateStaticParams() {
  // セグメントの取りうる値を以下のように指定する
  return  [{seg:"seg1"}, ..., {seg:"segN"}];
}

export default function Page({ params }) {
  const { seg } = params
  // ...
}

generateStaticParams()関数の返り値には、{ セグメント名: 値 }の配列を指定する。キーに指定するセグメント名は、フォルダ名に指定した動的ルートセグメント名([xxx]みたいなやつ)と対応していなければならない。

また、各ページのパスは、{ params: { seg: "そのページのセグメント値" } }という形でページのコンポーネントに渡される。

ちなみにNext.jsの公式ドキュメントには以下のようなサンプルコードが記述されている。

js
// `app/blog/[slug]/page.js`

// Return a list of `params` to populate the [slug] dynamic segment
export async function generateStaticParams() {
  const posts = await fetch("https://.../posts").then((res) => res.json());

  return posts.map((post) => ({
    slug: post.slug,
  }));
}

// Multiple versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
export default function Page({ params }) {
  const { slug } = params;
  // ...
}

これがgenerateStaticParams()関数の基本的な使い方である。

引数

generateStaticParams()関数には、オプションでparamsという引数が渡される。

この引数は、上位のセグメントのlayout.tsx、あるいはpage.tsxgenerateStaticParams()関数が定義されている場合に、{ params: { 親セグメント名:"親セグメント値" } }を返す。階層が深い場合は、ご先祖セグメント名とその値のキーバリューがすべて渡される。

自身が最上位の場合、{ params: {} }が返る。

返り値について

1階層だけを対象とする場合(=各階層でgenerateStaticParams()関数を定義する場合)は、先のように{ セグメント名: "値" }を指定すればいい。

ただ、ネストを1つのgenerateStaticParams()関数で賄うこともできる。この場合、返り値は以下のように指定する。

サンプルルート 返り値のパターン
app/[slug1]/[slug2] { slug1: string, slug2: string }[]
app/[...slug] { slug: string[] }[]

app/[slug1]/[slug2]のパターンでは、最下層のセグメント(この場合は[slug2])にあるlayout.tsxあるいはpage.tsxgenerateStaticParams()関数を定義する。

各階層には、対応するpage.tsxを用意しておく必要がある。

app/[...slug]のパターンでは、そのまま対象のセグメント(この場合は[...slug])の直下のlayout.tsxあるいはpage.tsxgenerateStaticParams()関数を定義する。

注意したいのは、子セグメントを指定したからといって親セグメントまで指定したことにはならない点である。例えば返り値に[{ slug:["seg1","seg2"] }]を渡した場合、/seg1/seg2はサポートされるが、/seg1はサポートされない。[{ slug:["seg1"] }, { slug:["seg1","seg2"] }]としてやる必要がある。

なお、こちらのパターンでは、動的ルートセグメント直下のpage.tsxが、以降のセグメントにも割り当てられる。

ハマったエラー0

bash
Error: The default export is not a React Component in page: "/[slug]"

該当の階層のlayout.tsx(あるいはpage.tsx)のfunction Layout()(あるいはfunction Page())が正しくエクスポートされていない。

定義とエクスポートに問題がなければ解消する。きっと。

ハマったエラー1

bash
⨯ Error: Page "/[xxx]/page" is missing param "/yyy" in "generateStaticParams()", which is required with "output: export" config.
    at DevServer.renderToResponseWithComponentsImpl

このエラーは、[xxx]というフォルダ下にあるpage.tsxgenerateStaticParams()の返り値に{ xxx: "yyy" }が含まれていないと発生する。

プロパティのキー(xxx)がセグメント名([xxx])と対応しているか。あるいは、アクセスしたいページのセグメントが"yyy"と対応しているかを確認するとよい。対応させれば解消する。たぶん。

ハマったエラー2

動的ルートをネストしているのに、子セグメントのgenerateStaticParams()が受け取る引数が空({ params:{} })になる。

原因不明だが、これは親ルートのpage.tsxgenerateStaticParams()関数を定義すると発生する。そのまま関数をlayout.tsxに移すと解消する。おそらく。

参考資料