Next.jsのメタデータAPI活用まとめ|SEOタグをコードで管理する
タイトル・description・OGP画像といったSEO関連のタグを、Next.jsのApp Routerでは「Metadata API」という仕組みでコードとして管理できます。この記事では、このブログで実際に使っている書き方をまとめます。
🛠 実装してみて分かったこと
WordPressのSEOプラグインだと管理画面から入力する項目が、Next.jsではすべてコードとして書けることに、最初は逆に戸惑いました。ですが慣れてくると、記事ごとにバラバラの書き方になりがちなプラグイン運用より、共通のルール(関数)に沿って機械的に生成できる分、抜け漏れが起きにくいと感じています。特にcanonical URLのような「絶対に統一したい項目」は、コードで一元管理できるメリットが大きいです。
実際にこの記事のページで生成された<head>の中身(SEO関連のタグだけ抜粋)はこちらです。
<title>Next.jsのメタデータAPI活用まとめ|SEOタグをコードで管理する|AIツクリラボ</title>
<meta name="description" content="Next.jsのApp Routerが提供するMetadata APIの使い方を..."/>
<meta name="robots" content="index, follow"/>
<link rel="canonical" href="https://aitsukurilabo.com/blog/nextjs-metadata-api-guide"/>
<meta property="og:title" content="Next.jsのメタデータAPI活用まとめ|SEOタグをコードで管理する"/>
<meta property="og:image" content=".../opengraph-image"/>
<meta property="og:type" content="article"/>
<meta name="twitter:card" content="summary_large_image"/>静的なページでの書き方
タイトルや説明文が固定のページ(このブログでは/aboutや/privacyなど)では、metadataというオブジェクトをそのままエクスポートするだけで済みます。
export const metadata: Metadata = {
title: "運営者情報",
description: `${siteConfig.name}の運営者情報です。`,
alternates: { canonical: `${siteConfig.url}/about` },
};サイト名やドメインを直接書かずsiteConfigという設定オブジェクトから参照しているのは、ドメイン変更やサイト名変更のときに直す箇所を1か所に集約するためです。
動的なページでの書き方
記事ページのように、URLごとに内容が変わるページでは、generateMetadataという非同期関数を使います。
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
try {
const post = await getPostBySlug(slug);
return {
title: post.title,
description: post.description,
alternates: {
canonical: `${siteConfig.url}/blog/${slug}`,
},
...(!isPublished(post) ? { robots: { index: false, follow: false } } : {}),
openGraph: {
title: post.title,
description: post.description,
type: "article",
publishedTime: post.date,
url: `${siteConfig.url}/blog/${slug}`,
},
};
} catch {
return {};
}
}記事データを取得する関数(getPostBySlug)をそのまま流用できるので、記事本文の表示とメタデータの生成でロジックが重複しない点も気に入っています。
robotsの行は、このサイト固有の判断です。予約投稿(未来日付)の記事に公開日より前に直接URLでアクセスされた場合に備え、isPublishedがfalseを返す記事にだけnoindexを追加しています。ここでrobotsキーごと条件分岐させているのは、Next.jsのmetadataがオブジェクトの値をundefinedにしても親の設定を丸ごと打ち消してしまう仕様があるためで、公開済みの記事はrobotsキー自体を省略してルートのlayout.tsx側の設定(max-image-previewなど)をそのまま引き継がせています。予約投稿の仕組み自体はNext.jsブログに予約投稿機能を実装するで詳しく解説しています。
親子関係のあるレイアウトでの継承
Next.jsのMetadataは、layout.tsxで定義した値を土台に、各ページのmetadata・generateMetadataが上書きしていく形でマージされます。このブログでは、サイト全体で共通のデフォルト値をlayout.tsxに、記事ごとの個別情報を各ページに書く、という役割分担にしています。
// layout.tsx(共通のベース)
export const metadata: Metadata = {
metadataBase: new URL(siteConfig.url),
title: {
default: siteConfig.defaultTitle,
template: `%s|${siteConfig.name}`,
},
description: siteConfig.description,
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
"max-image-preview": "large",
},
},
openGraph: {
type: "website",
locale: siteConfig.locale,
siteName: siteConfig.name,
},
twitter: {
card: "summary_large_image",
site: siteConfig.twitter,
},
other: siteConfig.adsenseClientId
? { "google-adsense-account": `ca-${siteConfig.adsenseClientId}` }
: {},
};titleはページごとに%s|AIツクリラボという形式のタイトルに自動で組み立てられるテンプレート指定、robotsのmax-image-preview: "large"は記事のOGP画像をGoogle画像検索でできるだけ大きく表示させるための指定、otherはAdSense審査用の確認タグを、環境変数が未設定の間は出力しないための条件分岐です。
OGP画像も同じ仕組みで自動生成
このブログでは、opengraph-image.tsxというファイルを置くことで、SNSシェア時の画像もコードで自動生成しています。手作業で画像を用意する必要がないため、記事を追加するたびの作業が減っています。この仕組みをさらに掘り下げた解説はNext.jsでOGP画像を自動生成するにまとめています。
よくある質問
Q. metadataとgenerateMetadata、どちらを使うべきですか?
A. 内容が固定ならmetadata、記事データなど動的な内容によって変わるならgenerateMetadataを使います。
Q. canonical URLを設定し忘れるとどうなりますか? A. 同じ内容のページが複数のURLでアクセス可能な場合に、検索エンジンがどちらを正規のURLとして扱うか判断しづらくなることがあります。忘れずに設定するのが安全です。
Q. メタデータの設定ミスは検証できますか?
A. ブラウザの開発者ツールで実際に出力されたHTMLの<head>を確認するのが最も確実です。SNSシェア時の見え方は、各SNSが提供するデバッグツールでも確認できます。
まとめ
Next.jsのMetadata APIは、SEOに必要なタグをコードとして一元管理できる仕組みです。プラグインの管理画面を都度開く必要がなく、記事データの取得ロジックとも自然に統合できるため、慣れると手作業のSEOプラグイン運用より抜け漏れが少なく感じています。