メタデータ
Next.js には、SEO とウェブ共有性を向上させるためのメタデータ API が用意されています。この API を使用すると、HTML の head
要素内の meta
タグや link
タグといったアプリケーションのメタデータを定義できます。
アプリケーションにメタデータを追加する方法は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
関数と fetch
を使用します。
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
リクエストは、generateMetadata
、generateStaticParams
、レイアウト、ページ、サーバーコンポーネント間で同じデータに対して自動的にメモ化されます。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
セグメントに最も近いセグメントに向かって順番に評価されます。例えば:
app/layout.tsx
(ルートレイアウト)app/blog/layout.tsx
(ネストされたブログレイアウト)app/blog/[slug]/page.tsx
(ブログページ)
マージ
評価順序に従い、同じルート内の複数のセグメントからエクスポートされたメタデータオブジェクトは、浅くマージされてルートの最終的なメタデータ出力が形成されます。重複するキーは順序に基づいて置換されます。
つまり、openGraph
や robots
のようなネストされたフィールドを持つメタデータは、先のセグメントで定義されていても、最後に定義したセグメントによって上書きされます。
フィールドの上書き
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acmeは...',
},
}
export const metadata = {
title: 'Blog',
openGraph: {
title: 'Blog',
},
}
// 出力:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />
上記の例では:
app/layout.js
のtitle
はapp/blog/page.js
のtitle
で置換されます。app/blog/page.js
がopenGraph
メタデータを設定しているため、app/layout.js
のすべてのopenGraph
フィールドが置換されます。openGraph.description
が存在しないことに注意してください。
セグメント間でネストされたフィールドを共有しながら他のフィールドを上書きしたい場合は、それらを別の変数に抽出できます:
export const openGraphImage = { images: ['http://...'] }
import { openGraphImage } from './shared-metadata'
export const metadata = {
openGraph: {
...openGraphImage,
title: 'Home',
},
}
import { openGraphImage } from '../shared-metadata'
export const metadata = {
openGraph: {
...openGraphImage,
title: 'About',
},
}
上記の例では、OG 画像が app/layout.js
と app/about/page.js
間で共有され、タイトルは異なります。
フィールドの継承
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acmeは...',
},
}
export const metadata = {
title: 'About',
}
// 出力:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acmeは..." />
注意点
app/layout.js
のtitle
はapp/about/page.js
のtitle
で置換されます。app/about/page.js
がopenGraph
メタデータを設定していないため、app/layout.js
のすべてのopenGraph
フィールドが継承されます。
動的画像生成
ImageResponse
コンストラクタを使用すると、JSX と CSS で動的な画像を生成できます。これは Open Graph 画像や Twitter カードなどのソーシャルメディア画像を作成するのに便利です。
ImageResponse
はEdge Runtime を使用し、Next.js はキャッシュされた画像に適切なヘッダーを自動的に追加して、パフォーマンスを向上させ再計算を減らします。
使用するには、next/og
から ImageResponse
をインポートします:
import { ImageResponse } from 'next/og'
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
は Route Handlers やファイルベースのメタデータなど、他の Next.js API とうまく統合されます。例えば、opengraph-image.tsx
ファイルで ImageResponse
を使用して、ビルド時またはリクエスト時に動的に Open Graph 画像を生成できます。
ImageResponse
は flexbox や絶対位置指定、カスタムフォント、テキスト折り返し、中央揃え、ネストされた画像など、一般的な CSS プロパティをサポートしています。サポートされている CSS プロパティの完全なリストを確認。
豆知識:
- 例は Vercel OG Playground で確認できます。
ImageResponse
は @vercel/og、Satori、Resvg を使用して HTML と CSS を PNG に変換します。- Edge Runtime のみサポートされています。デフォルトの Node.js ランタイムでは動作しません。
- flexbox と CSS プロパティのサブセットのみサポートされています。高度なレイアウト(例:
display: grid
)は動作しません。- 最大バンドルサイズは
500KB
です。バンドルサイズには JSX、CSS、フォント、画像、その他のアセットが含まれます。制限を超える場合は、アセットのサイズを減らすか、実行時に取得することを検討してください。- サポートされているフォント形式は
ttf
、otf
、woff
のみです。フォント解析速度を最大化するため、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>
)
}
構造化データは Rich Results Test(Google 用)または一般的な Schema Markup Validator で検証およびテストできます。
TypeScript を使用する場合は、schema-dts
などのコミュニティパッケージで JSON-LD を型付けできます:
import { Product, WithContext } from 'schema-dts'
const jsonLd: WithContext<Product> = {
'@context': 'https://schema.org',
'@type': 'Product',
name: 'Next.js Sticker',
image: 'https://nextjs.org/imgs/sticker.png',
description: 'Dynamic at the speed of static.',
}