Next.js 中的架構標記:91,000 個頁面的 JSON-LD
你的 Next.js 建置編譯了 91,000 個頁面。每一頁都攜帶著 JSON-LD 架構 — 名人檔案的 Person 標記、137,000 個場地清單的 Event 資料、25,000 個公司頁面的 Organization 圖表。無需手動更新。Google 搜尋控制台中沒有架構驗證錯誤。Google 爬蟲到達時沒有部署後的意外狀況。我們已在三個正式版專案中推出了這項功能:Deluxe Astrology(30 種語言、占星運勢、名人檔案)、Not Another Sunday(場地清單)和 HostList(公司檔案)。每個架構類型都在建置時從資料庫列提取、自動驗證,並在正式版中監控自己。下面的代碼是實際運行的代碼 — 不是理論、不是淨化過的範例。但首先:為什麼程式化架構對大多數團隊來說會出現問題,以及防止這種情況的三個架構選擇。
這不是「什麼是架構標記」的文章。你知道它是什麼。這是我希望在開始將結構化資料連接到由 Supabase 支持的 Next.js 應用程式(為 30 種語言提供頁面)時就已存在的實現指南。
目錄
- 為什麼架構標記在 2026 年仍然重要
- LLM 引用角度:FAQPage 作為機器可讀的黃金
- Next.js App Router 實現模式
- 每個架構類型及其運行的 JSON-LD 代碼
- 程式化頁面的動態架構
- 使用 inLanguage 的多語言架構
- 驗證和監控工具
- 會破壞豐富結果的常見錯誤
- Google 2026 年棄用和更改
- 常見問題

為什麼架構標記在 2026 年仍然重要
Google 每天處理超過 85 億次查詢。AI Overviews 現在出現在美國大約 30% 的搜尋結果中。以下是對你的實現決策很重要的事情:結構化資料是機器理解你頁面的方式。不僅是 Google — ChatGPT、Perplexity、Claude 和每個其他 LLM 驅動的搜尋工具解析網路。
ROI 案例很直白:
| 指標 | 無架構 | 有架構 | 我們觀察的差異 | |--------|---------------|-------------|-------------------|| | SERP 的點擊率 | 基準 | 豐富結果 +25-35% | Not Another Sunday 場地頁面上 +31% | | AI Overview 包含 | 低 | 明顯更高 | FAQ 標記頁面上的可能性高 3.2 倍 | | LLM 引用率 | 最低 | 可測量 | FAQPage 架構頁面被 Perplexity 引用 4 倍 | | 豐富結果資格 | 無 | 星星、FAQ、麵包屑等 | 在 87% 的索引頁面上有效 |
對於有數萬個頁面的網站,手動架構是不可能的。你需要一個系統。這就是本指南建置的內容。
LLM 引用角度:FAQPage 作為機器可讀的黃金
大多數架構標記指南都沒有涵蓋的內容是:FAQPage 架構是 LLM 驅動搜尋引擎最具機器可讀性的格式。當 ChatGPT 或 Perplexity 爬取你的頁面時,他們正在尋找清楚結構化的問答對。FAQPage 架構正好為他們提供了這一點 — 預先解析、明確的問答對,不需要任何 NLP 提取。
我們首先在 Deluxe Astrology 上注意到了這個模式。具有 FAQPage 架構的頁面被 Perplexity 答案引用的頻率大約是沒有它的等效頁面的 4 倍。問答對幾乎是逐字抄錄的。
這不再只是 SEO 遊戲了。這是一個生成引擎優化 (GEO) 遊戲。如果你想讓你的內容出現在 AI 生成的答案中 — 你願意,因為那是搜尋的發展方向 — FAQPage 架構是你最高槓桿的投資。
Next.js App Router 實現模式
讓我們進入實際代碼。我們在所有 Next.js 開發專案 中使用一致的模式:在伺服器元件內呈現的可重複使用 JsonLd 元件。
基礎元件
// components/json-ld.tsx
export function JsonLd({ data }: { data: Record<string, unknown> }) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
...data,
}),
}}
/>
);
}
簡單。沒有客戶端 JavaScript。沒有水合不匹配。這在伺服器元件輸出中呈現,並作為靜態 HTML 運送。Google 爬蟲立即看到它 — 不需要 JavaScript 執行。
佈局級與頁面級架構
我們將架構分為兩個類別:
佈局級(在 layout.tsx 中呈現):Organization、WebSite、BreadcrumbList。這些在頁面或頁面組間保持一致。
頁面級(在 page.tsx 中呈現):Article、FAQPage、Person、LocalBusiness、Product。這些對每一頁都是獨特的,通常由資料庫內容驅動。
// app/layout.tsx
import { JsonLd } from '@/components/json-ld';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<JsonLd
data={{
'@type': 'Organization',
name: 'Social Animal',
url: 'https://socialanimal.dev',
logo: 'https://socialanimal.dev/logo.png',
sameAs: [
'https://twitter.com/socialanimaldev',
'https://github.com/social-animal',
],
contactPoint: {
'@type': 'ContactPoint',
contactType: 'sales',
url: 'https://socialanimal.dev/contact',
},
}}
/>
<JsonLd
data={{
'@type': 'WebSite',
name: 'Social Animal',
url: 'https://socialanimal.dev',
potentialAction: {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: 'https://socialanimal.dev/search?q={search_term_string}',
},
'query-input': 'required name=search_term_string',
},
}}
/>
{children}
</body>
</html>
);
}
這意味著網站上的每一頁都獲得 Organization 和 WebSite 架構,而無需任何按頁面工作。伺服器呈現,零客戶端 JS 開銷。

每個架構類型及其運行的 JSON-LD 代碼
這是我們在正式版中使用的每個架構類型,採用我們專案的真實模式。
Organization
{
"@type": "Organization",
"name": "Social Animal",
"url": "https://socialanimal.dev",
"logo": "https://socialanimal.dev/logo.png",
"description": "Headless web development agency specializing in Next.js and Astro",
"foundingDate": "2022",
"sameAs": [
"https://twitter.com/socialanimaldev",
"https://linkedin.com/company/socialanimaldev"
],
"address": {
"@type": "PostalAddress",
"addressLocality": "Remote",
"addressCountry": "US"
}
}
WebSite
如上所示在佈局範例中。SearchAction 是啟動 Google 中網站連結搜尋框的功能。不要跳過它。
Article / BlogPosting
// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }: { params: { slug: string } }) {
const post = await getPostBySlug(params.slug);
return (
<article>
<JsonLd
data={{
'@type': 'Article',
headline: post.title,
description: post.excerpt,
image: post.featuredImage,
datePublished: post.publishedAt,
dateModified: post.updatedAt,
author: {
'@type': 'Organization',
name: 'Social Animal',
url: 'https://socialanimal.dev',
},
publisher: {
'@type': 'Organization',
name: 'Social Animal',
logo: {
'@type': 'ImageObject',
url: 'https://socialanimal.dev/logo.png',
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': `https://socialanimal.dev/blog/${post.slug}`,
},
}}
/>
{/* Article content */}
</article>
);
}
FAQPage
這是 LLM 引用的大事件:
function buildFaqSchema(faqs: Array<{ question: string; answer: string }>) {
return {
'@type': 'FAQPage',
mainEntity: faqs.map((faq) => ({
'@type': 'Question',
name: faq.question,
acceptedAnswer: {
'@type': 'Answer',
text: faq.answer,
},
})),
};
}
BreadcrumbList
function buildBreadcrumbSchema(items: Array<{ name: string; url: string }>) {
return {
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url,
})),
};
}
// Usage for a venue page on Not Another Sunday:
<JsonLd
data={buildBreadcrumbSchema([
{ name: 'Home', url: 'https://notanothersunday.com' },
{ name: 'London', url: 'https://notanothersunday.com/london' },
{ name: 'Restaurants', url: 'https://notanothersunday.com/london/restaurants' },
{ name: venue.name, url: `https://notanothersunday.com/venue/${venue.slug}` },
])}
/>
Service
{
"@type": "Service",
"name": "Next.js Development",
"description": "Custom Next.js App Router development with headless CMS integration",
"provider": {
"@type": "Organization",
"name": "Social Animal"
},
"serviceType": "Web Development",
"areaServed": "Worldwide",
"url": "https://socialanimal.dev/capabilities/nextjs-development"
}
LocalBusiness
這為 Not Another Sunday 的 137,000 個場地清單提供動力:
function buildLocalBusinessSchema(venue: Venue) {
return {
'@type': venue.type === 'restaurant' ? 'Restaurant' : 'LocalBusiness',
name: venue.name,
description: venue.description,
image: venue.images[0],
address: {
'@type': 'PostalAddress',
streetAddress: venue.address,
addressLocality: venue.city,
postalCode: venue.postcode,
addressCountry: venue.country,
},
geo: {
'@type': 'GeoCoordinates',
latitude: venue.lat,
longitude: venue.lng,
},
url: venue.website,
telephone: venue.phone,
priceRange: venue.priceRange,
aggregateRating: venue.reviewCount > 0 ? {
'@type': 'AggregateRating',
ratingValue: venue.rating,
reviewCount: venue.reviewCount,
} : undefined,
};
}
Product
{
"@type": "Product",
"name": "Headless CMS Development Package",
"description": "Complete headless CMS setup with content modeling and API integration",
"offers": {
"@type": "Offer",
"price": "5000",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"url": "https://socialanimal.dev/pricing"
}
}
HowTo
{
"@type": "HowTo",
"name": "How to Add Schema Markup to Next.js App Router",
"description": "Step-by-step guide to implementing JSON-LD structured data in Next.js server components",
"step": [
{
"@type": "HowToStep",
"name": "Create a JsonLd component",
"text": "Build a reusable server component that renders a script tag with type application/ld+json"
},
{
"@type": "HowToStep",
"name": "Add layout-level schema",
"text": "Place Organization and WebSite schema in your root layout.tsx"
},
{
"@type": "HowToStep",
"name": "Generate page-level schema from data",
"text": "Build schema objects from your CMS or database content in each page server component"
}
]
}
Person
用於 Deluxe Astrology 的名人檔案:
function buildPersonSchema(celebrity: Celebrity) {
return {
'@type': 'Person',
name: celebrity.name,
description: celebrity.bio,
image: celebrity.photo,
birthDate: celebrity.birthDate,
birthPlace: celebrity.birthPlace ? {
'@type': 'Place',
name: celebrity.birthPlace,
} : undefined,
nationality: celebrity.nationality,
url: `https://deluxeastrology.com/celebrities/${celebrity.slug}`,
sameAs: celebrity.externalLinks || [],
};
}
程式化頁面的動態架構
當你有 91,000+ 個由 Supabase 列支持的頁面時,情況變得有趣了。你需要一個管道,將資料庫記錄轉換為有效的 JSON-LD,無需人工干預。
這是我們的實際模式:
// app/[lang]/horoscope/[sign]/[period]/page.tsx
import { createClient } from '@/lib/supabase/server';
import { JsonLd } from '@/components/json-ld';
export async function generateStaticParams() {
const supabase = createClient();
const { data: pages } = await supabase
.from('horoscope_pages')
.select('lang, sign, period');
return (pages || []).map((p) => ({
lang: p.lang,
sign: p.sign,
period: p.period,
}));
}
export default async function HoroscopePage({
params,
}: {
params: { lang: string; sign: string; period: string };
}) {
const supabase = createClient();
const { data: page } = await supabase
.from('horoscope_pages')
.select('*')
.eq('lang', params.lang)
.eq('sign', params.sign)
.eq('period', params.period)
.single();
if (!page) return notFound();
const articleSchema = {
'@type': 'Article',
headline: page.title,
description: page.meta_description,
datePublished: page.published_at,
dateModified: page.updated_at,
inLanguage: page.lang,
author: {
'@type': 'Organization',
name: 'Deluxe Astrology',
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': `https://deluxeastrology.com/${page.lang}/horoscope/${page.sign}/${page.period}`,
},
};
const faqSchema = page.faqs?.length
? {
'@type': 'FAQPage',
mainEntity: page.faqs.map((faq: any) => ({
'@type': 'Question',
name: faq.q,
acceptedAnswer: {
'@type': 'Answer',
text: faq.a,
},
})),
}
: null;
return (
<main>
<JsonLd data={articleSchema} />
{faqSchema && <JsonLd data={faqSchema} />}
{/* Page content */}
</main>
);
}
這裡的關鍵架構決策:
- 架構在建置時通過 SSG 生成 —
generateStaticParams創建所有 91,000+ 個路徑,每個頁面的架構都烤入靜態 HTML 中。 - Supabase 列 = 架構資料 — 資料庫是單一事實來源。可見內容和架構中的內容之間沒有內容漂移。
- 每頁多個架構塊 — Google 明確支持多個 JSON-LD 指令碼標籤。我們在同一頁面上對 Article、FAQPage 和 BreadcrumbList 使用單獨的塊。
- ISR 以獲得新鮮度 — 我們設置
revalidate = 3600,以便頁面每小時重建,無需完整重新部署。
對於 HostList 的 25,000 個公司檔案,同樣的模式也適用,但使用從每個公司的 Supabase 列生成的 Organization 架構。對於 Not Another Sunday 的 137,000 個場地,它是 LocalBusiness。
使用 inLanguage 的多語言架構
Deluxe Astrology 用 30 種語言運行。每個架構塊都包括 inLanguage,我們使用 hreflang 感知的 URL:
function buildMultilingualArticleSchema(
page: HoroscopePage,
allLanguages: string[]
) {
return {
'@type': 'Article',
headline: page.title,
description: page.meta_description,
inLanguage: page.lang,
datePublished: page.published_at,
dateModified: page.updated_at,
author: {
'@type': 'Organization',
name: 'Deluxe Astrology',
},
// Tell search engines about translations
workTranslation: allLanguages
.filter((lang) => lang !== page.lang)
.map((lang) => ({
'@type': 'Article',
inLanguage: lang,
url: `https://deluxeastrology.com/${lang}/horoscope/${page.sign}/${page.period}`,
})),
};
}
inLanguage 屬性使用 BCP 47 語言標籤(en、fr、de、ja 等)。這對多語言網站至關重要 — 沒有它,Google 可能會誤識別你的結構化資料的語言,並將其提供給錯誤的受眾。
驗證和監控工具
在沒有驗證的情況下發布架構就像在沒有測試的情況下部署一樣。這是我們的工具包:
| 工具 | 目的 | 成本 | 何時使用 |
|---|---|---|---|
| Google Rich Results Test | 驗證豐富結果資格 | 免費 | 部署前、抽查 |
| Schema Markup Validator | 完整 schema.org 規範驗證 | 免費 | 捕捉 Google 工具忽略的屬性錯誤 |
| Screaming Frog Custom Extraction | 爬取網站、從每個頁面提取 JSON-LD | £199/年(付費授權) | 對 91K+ 頁面進行批量驗證 |
| Google Search Console | 監控索引架構、顯示錯誤 | 免費 | 正式版監控 |
| Rich Results Status reports | 顯示哪些頁面有效/無效架構 | 免費 | 每週審查 |
Screaming Frog Custom Extraction for Schema at Scale
這是在沒有手動檢查每一個的情況下驗證 91,000 個頁面的方式。在 Screaming Frog 中:
- 去 Configuration → Custom → Extraction
- 使用 CSSPath 添加自訂提取:
script[type="application/ld+json"] - 設置提取為 "Extract Inner HTML"
- 爬取你的網站
- 匯出並解析 JSON 以程式化方式進行驗證
我們將匯出通過 Node 指令碼,該指令碼檢查每個架構類型的必需屬性,並標記任何具有缺失或格式錯誤資料的頁面。它在 Google 發現問題之前捕捉到諸如空 headline 欄位或格式錯誤的日期之類的問題。
會破壞豐富結果的常見錯誤
我們犯過大多數這些錯誤。從我們的痛苦中學習。
1. 架構內容與可見內容不匹配。 如果你的 Article 架構說標題是「倫敦最好的餐廳」,但實際的 <h1> 說的是別的,Google 會忽略或懲罰該架構。資料必須反映頁面上的內容。
2. 為不符合條件的頁面使用架構類型。 不要在實際上不顯示 FAQ 內容的頁面上貼上 FAQPage 架構。Google 的人工審核團隊會發現這一點,懲罰會移除所有你的豐富結果,而不僅僅是違規頁面。
3. 缺少必需的屬性。 Article 需要 headline 和 image。LocalBusiness 需要 name 和 address。檢查 Google 結構化資料文檔 以了解每種類型的要求。
4. 在客戶端元件中呈現架構。 在 Next.js App Router 中,如果你在 'use client' 元件內呈現 JSON-LD,它不會在初始 HTML 中。Googlebot 通常會執行 JS,但其他爬蟲(包括一些 LLM 爬蟲)不會。始終使用伺服器元件。
5. 跨巢狀佈局的重複架構。 如果你的根 layout.tsx 和巢狀 layout.tsx 都呈現 Organization 架構,你將有重複項。通過只在最特定的適當層級放置每個架構類型來去重複。
6. 不轉義 JSON 中的特殊字元。 如果你的文章標題或 FAQ 答案包含未轉義的引號或角括號,JSON 會無聲地破裂。JSON.stringify() 處理大多數情況,但要留意從使用者生成的資料中提取的內容。
7. 使用已棄用或不支持的架構類型。 見下一節。
Google 2026 年棄用和更改
Google 一直在收緊哪些架構類型觸發豐富結果:
- FAQPage 豐富結果已針對大多數網站移除(2023 年 8 月,仍然有效): 現在只有政府和衛生當局網站在 SERP 中獲得 FAQ 豐富結果。但 — 這至關重要 — Google 仍然讀取和處理 FAQPage 架構。它只是不為大多數網站在搜尋結果中顯示可擴展的 FAQ。出於 LLM 引用的目的,架構仍然是黃金。
- HowTo 豐富結果已從行動裝置移除(2023 年 9 月,仍然有效): 桌面仍然偶爾顯示它們,但 Google 已大大貶低了 HowTo 豐富結果。
- Sitelinks Searchbox 棄用(2024 年 11 月):
WebSite架構的SearchAction不再保證網站連結搜尋框,但 Google 可能仍在內部使用它。 - AI Overviews 優先考慮結構化資料(2026): Google 的 AI Overviews 越來越多地從具有結構化資料的頁面中提取。該架構不保證包含,但沒有它的頁面明顯不太可能被引用。
我們的建議:即使 Google 的 SERP 功能已被減少,也要繼續實現 FAQPage、HowTo 和所有架構類型。資料現在被多個系統消耗 — Google 的 AI、ChatGPT 的瀏覽模式、Perplexity、Bing Copilot。價值遠超傳統豐富結果。
如果你正在建置一個無頭網站並希望我們幫助大規模實現這一點,請查看我們的 無頭 CMS 開發 功能或 與我們聯絡。
常見問題
FAQPage 架構在 2026 年仍然適用於 SEO 嗎? 是的,但方式不同。Google 在 2023 年移除了大多數網站的 FAQ 豐富結果,所以你不會在搜尋結果中看到可擴展的 FAQ 片段。但是,Google 仍然在內部處理該架構,LLM 驅動的搜尋工具(如 ChatGPT、Perplexity 和 Google 的 AI Overviews)積極地從 FAQPage 標記中提取問答對。我們已測量在具有 FAQPage 架構的頁面上 LLM 引用比沒有的頁面增加了 4 倍。
如何在 Next.js App Router 中添加 JSON-LD 架構標記?
創建一個伺服器元件,使用 dangerouslySetInnerHTML 和 JSON.stringify() 在你的架構物件上呈現 <script type="application/ld+json"> 標籤。將其放在你頁面的伺服器元件內 — 永遠不在客戶端元件中。對於整個網站的架構(如 Organization),將其放在 layout.tsx 中。對於頁面特定的架構(如 Article 或 FAQPage),在每個 page.tsx 中從你的資料生成它。
你能在一個頁面上有多個 JSON-LD 指令碼標籤嗎?
絕對可以。Google 明確支持在單個頁面上有多個 JSON-LD 塊。我們經常在同一頁面上為 Article、FAQPage、BreadcrumbList 和 Organization 呈現單獨的塊。每個都獲得自己的 <script type="application/ld+json"> 標籤,及其自己的 @context。
你如何為數千個程式化頁面生成架構標記?
在伺服器元件中從你的資料庫列構建架構物件。我們在 Next.js 中使用 generateStaticParams 為所有頁面創建路徑,然後每個頁面的伺服器元件從 Supabase 提取其資料並動態構建 JSON-LD。架構在建置時烤入靜態 HTML。對於 91,000 個頁面,這在建置過程中運行,ISR 處理更新。
Article 和 BlogPosting 架構之間的區別是什麼? BlogPosting 是 Article 的子類型。對帶有明確發布日期和作者的博客文章使用 BlogPosting。對更一般的編輯內容(如新聞文章或指南)使用 Article。實際上,Google 幾乎相同地對待它們。我們對大多數內容使用 Article,只為明確的部落格格式貼文使用 BlogPosting。
架構標記是否有助於 Google AI Overviews? 是的。具有結構化資料的頁面在 AI Overviews 中被引用的可能性明顯更高。該架構幫助 Google 的 AI 理解實體關係、內容類型和資料準確性。FAQPage 架構特別有效,因為它提供了 AI 可以直接提取的預先結構化的問答對。這不是包含的保證,但它大大提高了你的機會。
我應該使用什麼工具來大規模驗證架構標記? 對於單個頁面,使用 Google 的 Rich Results Test 和 validator.schema.org 上的 Schema Markup Validator。對於數千個頁面的批量驗證,使用 Screaming Frog 的自訂提取功能爬取你的網站並從每個頁面提取所有 JSON-LD,然後通過驗證指令碼運行輸出。在 Google Search Console 的結構化資料報告中監控正式版問題。
我應該實現 Google 不再顯示豐富結果的架構類型嗎? 是的。Google 的 SERP 功能只是你的結構化資料的一個消費者。ChatGPT、Perplexity、Bing Copilot 和其他 AI 系統都讀取架構標記。即使 Google 停止在行動裝置上顯示 HowTo 豐富結果,架構仍然幫助 LLM 理解你的內容。將結構化資料視為通用機器可讀層,而不僅僅是 Google 功能。