Next.jsのApp Routerで、headタグ内の情報(metadata)を管理する
Next.jsのApp Routerでは、ページファイル内でheadタグ要素を定義することができます。このサンプルでは、titleとmeta descriptionがheadタグ内に出力されます。また、WordPressやmicroCMSなどでコンテンツ情報を管理している場合、metadataの情報も非同期処理が必要になります。generateMetadata()関数を使用することで、非同期にメタデータを設定することができます。ただし、同一レベルでmetadataとgenerateMetadata()を同時に利用することはできません。metadata情報の管理を表示と切り離すことは便利ですが、同じAPIを呼び出す場合の対策については調査が必要です。Vercelを使用する場合は、fetchのキャッシュ設定も考慮する必要があります。
目次
Next.jsのApp Routerでは、 ページファイル(app/PATH/page.tsx
)内で特定の関数または変数をexportして headタグ要素を定義できます。
import type { Metadata } from 'next'
import { getSiteInfo } from './libs/microcms'
import { Header } from './components/layouts/Header'
import { Footer } from './components/layouts/Footer'
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const siteInfo = await getSiteInfo()
return (
<html lang='ja'>
<body>
<Header siteInfo={siteInfo} />
<main className='flex min-h-screen flex-col items-center justify-between p-24'>
{children}
</main>
<Footer siteInfo={siteInfo} />
</body>
</html>
)
}
このサンプルでは、title
とmeta descripton
の2つがheadタグ内に出力されます。
ページのメタデータを動的に設定する
WordPressやmicroCMSなどでコンテンツ情報を管理している場合、metadataの情報も非同期処理が必要です。
非同期に設定する場合は、generateMetadata()
を使います。
export async function generateMetadata(): Promise<Metadata>{
return {
title: 'Home'
}
}
親の値を取得して利用する
サイト名など、ルートレベルなどで定義した情報もgenerateMetadata
で利用できます。
第二引数(型ResolvingMetadata
)にPromiseで渡ってきますので、await
して取得・利用しましょう。
type PageProps = {
params: {
id: string;
},
searchParams: {
draftKey?: string;
}
}
export async function generateMetadata({params}: PageProps, parent: ResolvingMetadata):Promise<Metadata> {
const product = await getProductById(params.id)
const {title} = await parent
return {
title: `${product.name} | ${title?.absolute}`
}
}
同一レベルでmetadata
とgenerateMetadata()
の併用はNG(らしい)
同じファイルにexport const metadata
とexport async function generateMetadata
を書くとエラーが出ます。
"metadata" and "generateMetadata" cannot be exported at the same time, please keep one of them. Read more: https://nextjs.org/docs/app/api-reference/file-conventions/metadata
- OKだったケース:
app/layout.tsx
でgenerateMetadata()
を利用app/products/page.tsx
でmetadata
を利用
- NGだったケース:
app/layout.tsx
でgenerateMetadata()
を利用app/page.tsx
でmetadata
を利用
今の所「同一レベルでなければ、コンフリクトではなく上書きする・されるの関係になるからセーフ」という理解をしています。
おわりに
metadata情報の管理を表示系と切り離して行えるのはなかなか便利そうです。
ただ、「Viewとmetadataで同じAPIをそれぞれ呼び出す必要が出るケース」が起こりうるのかや、その場合の対策などは、もう少し調べてみたいと思います。
おそらくですが、Vercelを使う場合はfetch
のキャッシュ設定でよしなにする・・・ような気がしますが。
参考記事
https://nextjs.org/docs/app/building-your-application/optimizing/metadata