動的メタデータ

商品ページのタイトルが全部同じ

前回、ルートレイアウトに metadata を書いてタイトルを付けました。ところが商品詳細ページを開いても、タブの文字は fleama のままです。スニーカーの画面も革靴の画面も同じタイトルなので、タブを何枚も開くと見分けが付きません。検索結果でも同じ見出しが並ぶことになります。

商品ごとに違うタイトルを出したいのですが、export const metadata は固定の値しか書けません。商品名は URL の id を見ないと分からないので、この形では間に合いません。

値ではなく関数で返す

そこで使うのが generateMetadata です。metadata の代わりに、この名前の関数を export します。

export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { const { id } = await params; return { title: id + " 番の商品 | fleama" }; }

受け取るものは、ページを描く関数と同じ params です。第2章でやったとおり await してから中身を取り出します。データベースや API から取ってくる場合は、この関数の中で await fetch(...) を書けます。

戻り値の形は metadata と同じオブジェクトです。つまり「固定なら metadata、URL やデータで変わるなら generateMetadata」という使い分けになります。両方を同じファイルに書くことはできません。

見つからなかったとき

存在しない商品の URL を開かれることもあります。そのときは商品名が取れないので、代わりの文字を返しておきます。

if (!item) return { title: "見つかりません | fleama" };

ページ本体で notFound() を呼ぶのと合わせて、タイトルの側も逃げ道を用意しておくと画面と食い違いません。

演習

商品詳細ページに generateMetadata を足して、商品名の入ったタイトルを出します。

ヒント

app/items/[id]/page.tsx
layout.tsx
app/page.tsx
プレビュー

できているか

  • generateMetadata を書いている
  • params を await して id を取り出している
  • トップではレイアウトのタイトルが出ている
  • 商品ページのタイトルに商品名が入る