🔍 進階 SEO 実戦:動的 Metadata と Open Graph
もしあなたが超クールなブログやECサイトを作ったのに、FacebookやLINEで共有したときに醜いURLだけが表示され、美しいサムネイルとタイトルが表示されないなら、クリック率は確実に悲惨なものになるでしょう。
従来のReact(クライアントサイドレンダリング)時代では、この問題を解決するのは非常に苦痛で、プリレンダリングのために追加のサーバーやパッケージに依存する必要がありました。 しかし、Next.js App Routerの世界では、SEO最適化が組み込まれており、信じられないほど簡単に実装できます!
この章では、Metadata APIを徹底的にマスターし、動的にタイトルや説明文を生成する方法、そして自動的にソーシャル共有プレビュー画像(Open Graph Image)を生成する方法を教えます。
1. 静的 Metadata 設定
ホームページや特定の固定ページにタイトルを設定するだけなら、そのページのpage.tsxまたはlayout.tsxでmetadata定数をエクスポートするだけで済みます。
// src/app/layout.tsx または src/app/page.tsx
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Vibe Tutor 知識課金プラットフォーム',
description: '台湾で最も強力なSaaS実戦開発コース。Next.jsでネット印鑑機を構築する方法を教えます!',
keywords: ['Next.js', 'React', 'SaaS', '教育', 'Vibe Tutor'],
authors: [{ name: 'Vibe Coder' }],
// Open Graph 設定(Facebook、LINE用)
openGraph: {
title: 'Vibe Tutor 知識課金プラットフォーム',
description: '台湾で最も強力なSaaS実戦開発コース',
url: 'https://vibe-tutor.com',
siteName: 'Vibe Tutor',
images: [
{
url: 'https://vibe-tutor.com/og-image.jpg',
width: 1200,
height: 630,
alt: 'Vibe Tutor コースプレビュー画像',
},
],
locale: 'zh_TW',
type: 'website',
},
// Twitter 設定
twitter: {
card: 'summary_large_image',
title: 'Vibe Tutor 知識課金プラットフォーム',
description: '台湾で最も強力なSaaS実戦開発コース',
creator: '@vibecoder',
images: ['https://vibe-tutor.com/twitter-image.jpg'],
},
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
// ...
}
💡 実戦テクニック:タイトルテンプレート
layout.tsxでタイトルテンプレートを設定すると、子ページで毎回ブランド名を繰り返す必要がなくなります:
// src/app/layout.tsx
export const metadata: Metadata = {
title: {
template: '%s | Vibe Tutor', // 子ページのタイトルが%sを置換
default: 'Vibe Tutor 知識課金プラットフォーム', // 子ページで設定がない場合に使用
},
};
// src/app/pricing/page.tsx
export const metadata: Metadata = {
title: '価格プラン', // 最終的なタイトルは「価格プラン | Vibe Tutor」になる
};
2. 動的 Metadata (generateMetadata)
ブログ記事ページ/blog/[slug]/page.tsxのように、各記事のタイトルが異なる場合、どうすればよいでしょうか?
この場合、定数metadataは使えません。代わりに非同期関数generateMetadataをエクスポートする必要があります。
Next.jsはページをレンダリングする「前に」この関数を実行し、データベースからデータを取得して、対応する<title>と<meta>タグをHTMLの<head>に生成します!
// src/app/blog/[slug]/page.tsx
import { Metadata, ResolvingMetadata } from 'next';
import { db } from '@/lib/db'; // データベース
type Props = {
params: { slug: string }
};
// 動的 Metadata 生成
export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// 1. データベースから記事を検索
const post = await db.post.findUnique({ where: { slug: params.slug } });
// 記事が存在しない場合、404タイトルを返す
if (!post) {
return {
title: '記事が見つかりません',
};
}
// 親のMetadataを拡張(オプション)
const previousImages = (await parent).openGraph?.images || [];
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
// 記事にカバー画像があれば使用、なければ親のデフォルトを使用
images: post.coverImage ? [post.coverImage] : previousImages,
},
};
}
export default async function BlogPostPage({ params }: Props) {
// 実際のページコンテンツのレンダリングロジック
const post = await db.post.findUnique({ where: { slug: params.slug } });
// ...
}
💡 パフォーマンスの心配?Next.jsが解決済み!
「generateMetadataでデータベースを検索し、BlogPostPageでも同じ検索をするのはパフォーマンス的に無駄では?」と疑問に思うかもしれません。
心配無用です!Next.jsは内部でfetchキャッシュとReactのcacheメカニズムを使用しており、同じリクエスト内で同じURL/クエリは実際には一度しか実行されません。後続の呼び出しはキャッシュ結果を返すので、パフォーマンスに全く影響しません!
3. 究極の技:自動OG画像生成 (Image Generation)
VercelやGitHubの記事共有リンクで、「記事のタイトルが自動的に書かれた画像」を見たことがありますか? これは動的OG画像(Dynamic Open Graph Image)と呼ばれます。
以前はこの機能を実装するために、サードパーティAPIを利用したりHeadless Chromeでスクリーンショットを撮る必要があり、複雑すぎて諦めたくなるものでした。
今では、Next.jsがネイティブで@vercel/ogをサポートしており、JSXを書くだけで自動的に画像に変換してくれます!
ステップ1:opengraph-image.tsxファイルを作成
ルートフォルダ(例:src/app/blog/[slug]/)に、opengraph-image.tsx(または.alt.txt / .tsxとImageResponseの組み合わせ)という特別なファイルを作成します。
// src/app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og';
import { db } from '@/lib/db';
// 画像サイズの設定
export const runtime = 'edge';
export const alt = 'Blog Post Cover Image';
export const size = {
width: 1200,
height: 630,
};
export const contentType = 'image/png';
export default async function Image({ params }: { params: { slug: string } }) {
// 記事タイトルを取得
const post = await db.post.findUnique({ where: { slug: params.slug } });
const title = post?.title || '素晴らしい記事';
return new ImageResponse(
(
// ここにJSXを書く!画像に変換されます!
<div
style={{
fontSize: 80,
background: 'linear-gradient(to bottom right, #4f46e5, #ec4899)', // グラデーション
color: 'white',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '80px',
textAlign: 'center',
fontWeight: 800,
}}
>
{/* データベースから取得した動的タイトルを表示 */}
<div style={{ marginBottom: 40 }}>{title}</div>
{/* ブランドロゴやテキストを追加 */}
<div style={{ fontSize: 40, color: 'rgba(255,255,255,0.8)' }}>
🚀 Vibe Tutor 実戦教育
</div>
</div>
),
{
...size,
}
);
}
これだけです!設定不要で、誰かがFacebookに記事URLを貼り付けると:
https://your-domain.com/blog/my-awesome-post
Facebookのクローラーが自動的にこのJSXでリアルタイム生成されたグラデーション背景+大タイトルの美しい画像を読み取ります!
これがNext.jsがSEOとビジネスサイトに最適と言われる究極の理由です!MetadataとOG Imageをマスターすれば、あなたのサイトのトラフィックは確実に天井知らずになります!📈