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>
      )
    }
    

    このサンプルでは、titlemeta 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}`
      }
    }

    同一レベルでmetadatagenerateMetadata()の併用はNG(らしい)

    同じファイルにexport const metadataexport 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.tsxgenerateMetadata()を利用
      • app/products/page.tsxmetadataを利用
    • NGだったケース:
      • app/layout.tsxgenerateMetadata()を利用
      • app/page.tsxmetadataを利用

    今の所「同一レベルでなければ、コンフリクトではなく上書きする・されるの関係になるからセーフ」という理解をしています。

    おわりに

    metadata情報の管理を表示系と切り離して行えるのはなかなか便利そうです。

    ただ、「Viewとmetadataで同じAPIをそれぞれ呼び出す必要が出るケース」が起こりうるのかや、その場合の対策などは、もう少し調べてみたいと思います。

    おそらくですが、Vercelを使う場合はfetchのキャッシュ設定でよしなにする・・・ような気がしますが。

    参考記事

    https://nextjs.org/docs/app/building-your-application/optimizing/metadata

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark