Metadata
Next.jsには、アプリケーションのメタデータ(例:HTMLのhead要素内のmetaやlinkタグ)を定義するためのMetadata APIがあります。これにより、SEOやWebの共有性が向上します。
アプリケーションにメタデータを追加する方法は2つあります:
- Config-based Metadata:
layout.jsまたはpage.jsファイルで静的なmetadataオブジェクトまたは動的なgenerateMetadata関数をエクスポートします - File-based Metadata: 静的または動的に生成された特別なファイルをルートセグメントに追加します
これらのオプションの両方で、Next.jsはページに関連する<head>要素を自動的に生成します。また、ImageResponseコンストラクタを使用して動的なOG画像を作成することもできます。
Static Metadata
静的メタデータを定義するには、layout.jsまたは静的なpage.jsファイルからMetadataオブジェクトをエクスポートします。
- TypeScript
- JavaScript
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リファレンスを参照してください。
Dynamic Metadata
generateMetadata関数を使用して、動的な値を必要とするメタデータをfetchすることができます。
- TypeScript
- JavaScript
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: Promise<{ id: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// ルートパラメータを読み取る
const { id } = await params
// データを取得する
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 } = await params
// データを取得する
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リファレンスを参照してください。
Good to know:
generateMetadataを通じた静的および動的メタデータは、Server Componentsでのみサポートされています。fetchリクエストは、generateMetadata、generateStaticParams、Layouts、Pages、Server Components全体で同じデータに対して自動的にメモ化されます。fetchが利用できない場合は、Reactのcacheを使用することができます。- Next.jsは、
generateMetadata内でのデータ取得が完了するまで、UIをクライアントにストリーミングするのを待ちます。これにより、ストリーミングされたレスポンスの最初の部分に<head>タグが含まれることが保証されます。
File-based metadata
メタデータ用に利用可能な特別なファイルは以下のとおりです:
- favicon.ico, apple-icon.jpg, and icon.jpg
- opengraph-image.jpg and twitter-image.jpg
- robots.txt
- sitemap.xml
これらは静的メタデータとして使用することも、コードでプログラム的に生成することもできます。
実装と例については、Metadata Files APIリファレンスとDynamic Image Generationを参照してください。
Behavior
File-based metadataは優先度が高く、config-based metadataを上書きします。
Default Fields
ルートがメタデータを定義していない場合でも、常に追加される2つのデフォルトのmetaタグがあります:
- meta charset tagは、Webサイトの文字エンコーディングを設定します
- meta viewport tagは、異なるデバイスに合わせてWebサイトのビューポート幅とスケールを設定します
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
Good to know: デフォルトの
viewportメタタグを上書きすることができます。
Ordering
メタデータは、rootセグメントから最終的なpage.jsセグメントに最も近いセグメントまで順に評価されます。例:
app/layout.tsx(Root Layout)app/blog/layout.tsx(Nested Blog Layout)app/blog/[slug]/page.tsx(Blog Page)
Merging
評価順序に従って、同じルート内の複数のセグメントからエクスポートされたメタデータオブジェクトは浅くマージされ、ルートの最終的なメタデータ出力を形成します。重複するキーは、その順序に基づいて置き換えられます。
つまり、openGraphやrobotsのようなネストされたフィールドを持つメタデータは、以前のセグメントで定義されている場合、最後のセグメントによって上書きされます。
Overwriting fields
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
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/layout.jsのすべてのopenGraphフィールドは、app/blog/page.jsでopenGraphメタデータが設定されているため、app/blog/page.jsで置き換えられます。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の間で共有されており、タイトルは異なります。
Inheriting fields
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
export const metadata = {
title: 'About',
}
// 出力:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />
注意事項
app/layout.jsのtitleはapp/about/page.jsのtitleによって置き換えられます。app/about/page.jsでopenGraphメタデータが設定されていないため、app/layout.jsのすべてのopenGraphフィールドはapp/about/page.jsで継承されます。
Dynamic Image Generation
ImageResponseコンストラクタを使用すると、JSXとCSSを使用して動的な画像を生成できます。これは、Open Graph画像やTwitterカードなどのソーシャルメディア画像を作成するのに便利です。
使用するには、next/ogからImageResponseをインポートします:
import { ImageResponse } from 'next/og'
export default function OG() {
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やfile-based Metadataなど、他のNext.js APIとよく統合されます。たとえば、opengraph-image.tsxファイルでImageResponseを使用して、ビルド時またはリクエスト時に動的にOpen Graph画像を生成できます。
ImageResponseは、flexboxや絶対位置指定、カスタムフォント、テキストの折り返し、センタリング、ネストされた画像など、一般的なCSSプロパティをサポートしています。サポートされているCSSプロパティの完全なリストを参照してください。
Good to know:
- Vercel OG Playgroundで例を確認できます。
ImageResponseは、HTMLとCSSをPNGに変換するために@vercel/og、Satori、Resvgを使用します。- flexboxとCSSプロパティのサブセットのみがサポートされています。高度なレイアウト(例:
display: grid)は機能しません。- 最大バンドルサイズは
500KBです。バンドルサイズには、JSX、CSS、フォント、画像、その他のアセットが含まれます。制限を超えた場合は、アセットのサイズを減らすか、実行時に取得することを検討してください。ttf、otf、woffフォント形式のみがサポートされています。フォントの解析速度を最大化するために、ttfまたはotfがwoffよりも優先されます。
JSON-LD
JSON-LDは、検索エンジンがコンテンツを理解するために使用できる構造化データの形式です。たとえば、人、イベント、組織、映画、本、レシピ、その他多くの種類のエンティティを記述するために使用できます。
JSON-LDに関する現在の推奨事項は、layout.jsまたはpage.jsコンポーネント内で構造化データを<script>タグとしてレンダリングすることです。例:
- TypeScript
- JavaScript
export default async function Page({ params }) {
const { id } = await params
const product = await getProduct(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 { id } = await params
const product = await getProduct(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 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.',
}