🔍 進階 SEO 實戰:動態 Metadata 與 Open Graph
如果你架設了一個超酷的部落格或電商網站,但貼到 Facebook 或 LINE 群組時,只顯示一段醜醜的網址、沒有漂亮的縮圖與標題,那你的點擊率絕對會慘不忍睹。
在傳統 React (Client-side Rendering) 時代,要解決這個問題非常痛苦,你必須依賴額外的伺服器或是套件來做預先渲染 (Prerendering)。 但恭喜你,在 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 }) {
// ...
}
💡 實戰小技巧:Template 標題
你可以在 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: '文章找不到',
};
}
// Optionally access and extend (rather than replace) parent 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 機制,同一個 Request 裡面對同一個 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>
{/* 加上你的品牌 Logo 或文字 */}
<div style={{ fontSize: 40, color: 'rgba(255,255,255,0.8)' }}>
🚀 Vibe Tutor 實戰教學
</div>
</div>
),
{
...size,
}
);
}
就這樣!不用任何設定,只要有人在 Facebook 貼上你這篇文章的網址:
https://your-domain.com/blog/my-awesome-post
Facebook 的爬蟲就會自動來讀取這張由 JSX 即時運算出來的漸層底色+大標題的超美圖片!
這就是 Next.js 被譽為最適合做 SEO 與商業網站的終極原因!掌握了 Metadata 與 OG Image,你的網站流量保證能突破天際!📈