メタデータ

Next.jsには、SEOとウェブ共有性を向上させるためのメタデータ(HTMLのhead要素内のmetaタグやlinkタグなど)を定義できるメタデータAPIが用意されています。

アプリケーションにメタデータを追加する方法は2つあります:

  • 設定ベースのメタデータlayout.jsまたはpage.jsファイルで静的なmetadataオブジェクトまたは動的なgenerateMetadata関数をエクスポート
  • ファイルベースのメタデータ:ルートセグメントに静的または動的に生成された特別なファイルを追加

これらのオプションを使用すると、Next.jsは自動的にページに関連する<head>要素を生成します。ImageResponseコンストラクタを使用して動的なOG画像を作成することもできます。

静的メタデータ

静的メタデータを定義するには、layout.jsまたは静的なpage.jsファイルからMetadataオブジェクトをエクスポートします。

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}
export const metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}

利用可能なすべてのオプションについては、APIリファレンスを参照してください。

動的メタデータ

動的な値が必要なメタデータを取得するには、generateMetadata関数を使用できます。

import type { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // ルートパラメータを読み取り
  const id = params.id

  // データを取得
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // 必要に応じて親メタデータにアクセスして拡張(置換ではなく)
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }: Props) {}
export async function generateMetadata({ params, searchParams }, parent) {
  // ルートパラメータを読み取り
  const id = params.id

  // データを取得
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // 必要に応じて親メタデータにアクセスして拡張(置換ではなく)
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }) {}

利用可能なすべてのパラメータについては、APIリファレンスを参照してください。

豆知識:

  • generateMetadataを通じた静的および動的メタデータはサーバーコンポーネントでのみサポートされています。
  • fetchリクエストは、generateMetadatagenerateStaticParams、レイアウト、ページ、サーバーコンポーネント間で同じデータに対して自動的にメモ化されます。fetchが利用できない場合はReactのcacheを使用できます
  • Next.jsは、クライアントにUIをストリーミングする前にgenerateMetadata内のデータ取得が完了するのを待機します。これにより、ストリーミングレスポンスの最初の部分に<head>タグが含まれることが保証されます。

ファイルベースのメタデータ

メタデータ用に以下の特別なファイルが利用可能です:

これらは静的メタデータに使用できるほか、コードでプログラム的に生成することもできます。

実装と例については、メタデータファイルAPIリファレンスと動的画像生成を参照してください。

動作

ファイルベースのメタデータは優先度が高く、設定ベースのメタデータを上書きします。

デフォルトフィールド

ルートがメタデータを定義していなくても、常に追加される2つのデフォルトmetaタグがあります:

  • meta charsetタグはウェブサイトの文字エンコーディングを設定します。
  • meta viewportタグは、異なるデバイスに合わせてウェブサイトのビューポート幅とスケールを調整します。
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

豆知識:デフォルトのviewportメタタグは上書きできます。

評価順序

メタデータは、ルートセグメントから開始して最終的なpage.jsセグメントに最も近いセグメントまで、順番に評価されます。例えば:

  1. app/layout.tsx(ルートレイアウト)
  2. app/blog/layout.tsx(ネストされたブログレイアウト)
  3. app/blog/[slug]/page.tsx(ブログページ)

マージ

評価順序に従って、同じルートの複数のセグメントからエクスポートされたメタデータオブジェクトは、浅くマージされてルートの最終的なメタデータ出力が形成されます。重複するキーは順序に基づいて置換されます。

つまり、openGraphrobotsなどのネストされたフィールドを持つメタデータは、前のセグメントで定義されたものが、それらを定義する最後のセグメントによって上書きされます。

フィールドの上書き

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acmeは...',
  },
}
app/blog/page.js
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}

// 出力:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

上記の例では:

  • app/layout.jstitleapp/blog/page.jstitleによって置換されます。
  • app/blog/page.jsopenGraphメタデータを設定しているため、app/layout.jsのすべてのopenGraphフィールドが置換されます。openGraph.descriptionが存在しないことに注意してください。

セグメント間で一部のネストされたフィールドを共有しながら他のフィールドを上書きしたい場合は、それらを別の変数に抽出できます:

app/shared-metadata.js
export const openGraphImage = { images: ['http://...'] }
app/page.js
import { openGraphImage } from './shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'ホーム',
  },
}
app/about/page.js
import { openGraphImage } from '../shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
}

上記の例では、OG画像がapp/layout.jsapp/about/page.js間で共有され、タイトルが異なります。

フィールドの継承

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acmeは...',
  },
}
app/about/page.js
export const metadata = {
  title: 'About',
}

// 出力:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acmeは..." />

注意点

  • app/layout.jstitleapp/about/page.jstitleによって置換されます。
  • app/about/page.jsopenGraphメタデータを設定していないため、app/layout.jsのすべてのopenGraphフィールドが継承されます。

動的画像生成

ImageResponseコンストラクタを使用すると、JSXとCSSを使って動的な画像を生成できます。これはOpen Graph画像やTwitterカードなどのソーシャルメディア画像を作成するのに便利です。

ImageResponseEdge Runtimeを使用し、Next.jsはキャッシュされた画像に適切なヘッダーを自動的に追加して、パフォーマンスを向上させ、再計算を減らします。

使用するには、next/serverからImageResponseをインポートします:

app/about/route.js
import { ImageResponse } from 'next/server'

export const runtime = 'edge'

export async function GET() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          textAlign: 'center',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        Hello world!
      </div>
    ),
    {
      width: 1200,
      height: 600,
    }
  )
}

ImageResponseは、ルートハンドラやファイルベースのメタデータなど、他のNext.js APIとうまく統合されます。例えば、opengraph-image.tsxファイルでImageResponseを使用して、ビルド時またはリクエスト時に動的にOpen Graph画像を生成できます。

ImageResponseは、フレックスボックスや絶対位置指定、カスタムフォント、テキスト折り返し、中央揃え、ネストされた画像など、一般的なCSSプロパティをサポートしています。サポートされているCSSプロパティの完全なリストを参照してください

豆知識:

  • 例はVercel OG Playgroundで利用可能です。
  • ImageResponse@vercel/ogSatori、Resvgを使用してHTMLとCSSをPNGに変換します。
  • Edge Runtimeのみがサポートされています。デフォルトのNode.jsランタイムでは動作しません。
  • フレックスボックスとCSSプロパティのサブセットのみがサポートされています。高度なレイアウト(例:display: grid)は動作しません。
  • 最大バンドルサイズは500KBです。バンドルサイズにはJSX、CSS、フォント、画像、その他のアセットが含まれます。制限を超える場合は、アセットのサイズを減らすか、実行時に取得することを検討してください。
  • フォント形式はttfotfwoffのみがサポートされています。フォント解析速度を最大化するため、woffよりもttfまたはotfが推奨されます。

JSON-LD

JSON-LDは、検索エンジンがコンテンツを理解するために使用できる構造化データ形式です。例えば、人物、イベント、組織、映画、本、レシピなど、多くの種類のエンティティを記述するために使用できます。

現在のJSON-LDの推奨方法は、layout.jsまたはpage.jsコンポーネントで<script>タグとして構造化データをレンダリングすることです。例えば:

export default async function Page({ params }) {
  const product = await getProduct(params.id)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }

  return (
    <section>
      {/* ページにJSON-LDを追加 */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}
export default async function Page({ params }) {
  const product = await getProduct(params.id)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }

  return (
    <section>
      {/* ページにJSON-LDを追加 */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}

構造化データは、Googleのリッチリザルトテストまたは一般的なスキーママークアップバリデーターで検証およびテストできます。

schema-dtsなどのコミュニティパッケージを使用して、TypeScriptでJSON-LDを型付けできます:

import { Product, WithContext } from 'schema-dts'

const jsonLd: WithContext<Product> = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: 'Next.jsステッカー',
  image: 'https://nextjs.org/imgs/sticker.png',
  description: '静的な速度で動的に。',
}