我們在 91,000+ 程序化頁面上運送了結構化數據。不是打字錯誤。跨三個生產項目 — Deluxe Astrology(30 種語言、星座運勢、名人檔案)、Not Another Sunday(137,000 個場地列表)和 HostList(25,000 個公司檔案) — 我們構建了在構建時從數據庫行生成 JSON-LD 模式、自動驗證它並在生產中監控它的系統。這是我們學到的一切,蒸餾成你實際可以使用的工作代碼。

這不是一篇「什麼是模式標記」的文章。你知道它是什麼。這是我希望在開始將結構化數據連接到由 Supabase 支持的 Next.js 應用(為 30 種語言服務頁面)時就存在的實施指南。

目錄

Schema Markup in Next.js: JSON-LD Structured Data Guide for 2026

為什麼模式標記在 2026 年仍然很重要

Google 每天處理超過 85 億次查詢。AI 概覽現在出現在美國約 30% 的搜索結果中。對於你的實現決策很重要的事情是:結構化數據是機器理解你的頁面的方式。不僅僅是 Google — ChatGPT、Perplexity、Claude 和所有其他解析網絡的 LLM 驅動搜索工具。

ROI 案例很直接:

指標 無模式 有模式 我們觀察到的差異
從 SERP 的 CTR 基準線 +25-35%(帶豐富結果) Not Another Sunday 場地頁面上 +31%
AI 概覽包含 明顯更高 FAQ 註釋頁面上高 3.2 倍
LLM 引用率 最小 可測量 Perplexity 引用 FAQPage 模式頁面的頻率高 4 倍
豐富結果資格 星星、常見問題、麵包屑等 在 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 中呈現):組織、網站、麵包屑列表。這些在整個頁面或頁面組中是一致的。

頁面級別(在 page.tsx 中呈現):文章、常見問題頁面、人物、本地商業、產品。這些對於每個頁面都是唯一的,通常由數據庫內容驅動。

// 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>
  );
}

這意味著網站上的每一頁都獲得組織和網站模式,無需任何每頁工作。服務器呈現,零客戶端 JS 開銷。

Schema Markup in Next.js: JSON-LD Structured Data Guide for 2026 - architecture

每種模式類型與工作 JSON-LD 代碼

這是我們在生產中使用的每種模式類型,具有來自我們項目的真實模式。

組織

{
  "@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"
  }
}

網站

如上所示在佈局示例中。SearchAction 是為 Google 中的站點連結搜索框提供動力的。不要跳過它。

文章 / 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>
  );
}

常見問題頁面

這是 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,
      },
    })),
  };
}

麵包屑列表

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}` },
  ])}
/>

服務

{
  "@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"
}

本地商業

這為 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,
  };
}

產品

{
  "@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"
  }
}

如何做

{
  "@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"
    }
  ]
}

人物

在 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>
  );
}

關鍵的架構決策如下:

  1. 模式在構建時通過 SSG 生成generateStaticParams 創建所有 91,000+ 路徑,每個頁面的模式被烤入靜態 HTML 中。
  2. Supabase 行 = 模式數據 — 數據庫是單一的真實來源。可見內容和模式中的內容之間沒有內容漂移。
  3. 每頁多個模式塊 — Google 明確支持多個 JSON-LD 腳本標籤。我們在同一頁上為文章、常見問題頁面和麵包屑列表使用單獨的塊。
  4. ISR 用於新鮮度 — 我們設置 revalidate = 3600,以便頁面每小時重新構建,無需完整重新部署。

對於 HostList 的 25,000 個公司檔案,相同的模式適用,但以從每個公司的 Supabase 行生成的組織模式為準。對於 Not Another Sunday 的 137,000 個場地,它是本地商業。

帶有 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 語言標籤(enfrdeja 等)。對於多語言網站來說,這至關重要 — 沒有它,Google 可能會誤認你的結構化數據的語言並將其提供給錯誤的受眾。

驗證和監控工具

在沒有驗證的情況下運送模式就像在沒有測試的情況下部署一樣。這是我們的工具包:

工具 目的 成本 何時使用
Google 豐富結果測試 驗證豐富結果資格 免費 部署前,點檢查
模式標記驗證器 完整的 schema.org 規格驗證 免費 Google 工具忽略的捕獲屬性錯誤
Screaming Frog 自定義提取 爬取網站,從每一頁提取 JSON-LD £199/年(付費許可證) 跨 91K+ 頁面的批量驗證
Google Search Console 監控索引模式,展示錯誤 免費 正在進行的生產監控
豐富結果狀態報告 顯示哪些頁面有有效/無效模式 免費 每週審核

用於大規模模式的 Screaming Frog 自定義提取

這是如何在不手動檢查每一頁的情況下驗證 91,000 頁。在 Screaming Frog 中:

  1. 轉到配置 → 自定義 → 提取
  2. 添加 CSSPath 為 script[type="application/ld+json"] 的自定義提取
  3. 設置提取為「提取內部 HTML」
  4. 爬取你的網站
  5. 導出並解析 JSON 以進行編程驗證

我們通過 Node 腳本管道導出,該腳本檢查每個模式類型的必需屬性,並標記任何具有缺失或格式錯誤數據的頁面。它在 Google 發現問題之前捕獲它們,如空 headline 字段或日期格式錯誤。

會毀掉你的豐富結果的常見錯誤

我們犯過大部分這些。從我們的痛苦中學習。

1. 模式內容與可見內容不匹配。 如果你的文章模式說標題是「倫敦最好的餐廳」,但實際 <h1> 說的是不同的東西,Google 將忽略或懲罰模式。數據必須反映頁面上的內容。

2. 為不符合條件的頁面使用模式類型。 不要在不實際顯示常見問題內容的頁面上貼上 FAQPage 模式。Google 的手動操作團隊會發現這一點,懲罰會移除你的所有豐富結果,而不僅僅是冒犯的頁面。

3. 缺少必需的屬性。 文章需要 headlineimage。本地商業需要 nameaddress。檢查 Google 結構化數據文檔 中每種類型的要求。

4. 在客戶端組件中呈現模式。 在 Next.js App Router 中,如果你在 'use client' 組件內呈現 JSON-LD,它將不會在初始 HTML 中。Googlebot 通常會執行 JS,但其他爬蟲(包括一些 LLM 爬蟲)不會。始終使用服務器組件。

5. 在嵌套佈局中重複模式。 如果你的根 layout.tsx 和嵌套 layout.tsx 都呈現組織模式,你將有重複。通過僅在最具體的適當級別放置每個模式類型來去重。

6. 不轉義 JSON 中的特殊字符。 如果你的文章標題或常見問題答案包含未轉義的引號或角括號,JSON 會無聲地中斷。JSON.stringify() 處理大多數情況,但要注意從用戶生成的數據中提取的內容。

7. 使用已棄用或不支持的模式類型。 請參閱下一部分。

Google 2025-2026 棄用和變更

Google 一直在緊縮哪些模式類型觸發豐富結果:

  • FAQPage 豐富結果已從大多數網站中移除(2023 年 8 月,仍然有效): 現在只有政府和衛生當局網站在 SERP 中獲得常見問題豐富結果。但 — 這很重要 — Google 仍然閱讀和處理 FAQPage 模式。它只是不為大多數網站在搜索結果中顯示可展開的常見問題。出於 LLM 引用目的,模式仍然是黃金。
  • HowTo 豐富結果已從移動設備中移除(2023 年 9 月,仍然有效): 桌面仍然會偶爾顯示它們,但 Google 已大幅降低 HowTo 豐富結果的優先級。
  • Sitelinks Searchbox 棄用(2024 年 11 月): 網站模式的 SearchAction 不再保證 sitelinks searchbox,但 Google 可能仍然在內部使用它。
  • AI 概覽優先考慮結構化數據(2025-2026): Google 的 AI 概覽越來越多地從具有結構化數據的頁面中提取。模式不能保證包含,但沒有它的頁面被引用的可能性明顯更低。

我們的建議:即使 Google 的 SERP 功能已被減少,仍然繼續實現 FAQPage、HowTo 和所有模式類型。現在數據由多個系統使用 — Google 的 AI、ChatGPT 的瀏覽模式、Perplexity、Bing Copilot。價值遠超傳統豐富結果。

如果你正在構建無頭網站並想幫助大規模實現此功能,請檢查我們的 無頭 CMS 開發 功能或 聯繫我們

常見問題

FAQPage 模式在 2026 年仍然適用於 SEO 嗎? 是的,但方式不同。Google 在 2023 年移除了大多數網站的常見問題豐富結果,所以你不會在搜索結果中看到可展開的常見問題片段。然而,Google 仍然在內部處理模式,LLM 驅動搜索工具如 ChatGPT、Perplexity 和 Google 的 AI 概覽會積極從 FAQPage 標記中提取問答對。我們測量了帶有 FAQPage 模式的頁面與沒有模式的頁面的 LLM 引用增加了 4 倍。

如何在 Next.js App Router 中添加 JSON-LD 模式標記? 創建一個服務器組件,使用 dangerouslySetInnerHTMLJSON.stringify() 在模式對象上呈現 <script type="application/ld+json"> 標籤。將其放在你的頁面的服務器組件內 — 絕不在客戶端組件中。對於網站範圍的模式如組織,將其放在 layout.tsx 中。對於特定頁面的模式如文章或常見問題頁面,在每個 page.tsx 中從你的數據生成它。

你可以在一個頁面上有多個 JSON-LD 腳本標籤嗎? 絕對可以。Google 明確支持在單個頁面上有多個 JSON-LD 塊。我們在同一頁上常規呈現文章、常見問題頁面、麵包屑列表和組織的單獨塊。每個都獲得自己的 <script type="application/ld+json"> 標籤,具有自己的 @context

如何為數千個程序化頁面生成模式標記? 從你的數據庫行在服務器組件中構建模式對象。我們在 Next.js 中使用 generateStaticParams 為所有頁面創建路徑,然後每個頁面的服務器組件從 Supabase 獲取其數據並動態構造 JSON-LD。模式在構建時被烤入靜態 HTML。對於 91,000 頁,這在構建過程中執行,ISR 處理更新。

文章和 BlogPosting 模式之間的區別是什麼? BlogPosting 是文章的子類型。為明確出版日期和作者的博客文章使用 BlogPosting。為更常規的編輯內容(如新聞文章或指南)使用文章。在實踐中,Google 對它們的對待幾乎相同。我們對大多數內容使用文章,僅對明確博客格式化的文章使用 BlogPosting。

模式標記有助於 Google AI 概覽嗎? 有。帶有結構化數據的頁面在 AI 概覽中被引用的可能性明顯更高。模式幫助 Google 的 AI 理解實體關係、內容類型和數據準確性。FAQPage 模式特別有效,因為它提供 AI 可以直接提取的預結構化問答對。這不是包含的保證,但它顯著提高了你的機會。

我應該使用什麼工具大規模驗證模式標記? 對於單個頁面,使用 Google 的豐富結果測試和 validator.schema.org 處的模式標記驗證器。對於跨數千頁的批量驗證,使用 Screaming Frog 的自定義提取功能爬取你的網站並提取所有 JSON-LD,然後通過驗證腳本執行輸出。在 Google Search Console 的結構化數據報告中監控持續問題。

我應該實現 Google 不再為其顯示豐富結果的模式類型嗎? 有。Google 的 SERP 功能只是你的結構化數據的一個消費者。ChatGPT、Perplexity、Bing Copilot 和其他 AI 系統都閱讀模式標記。即使 Google 停止在移動設備上顯示 HowTo 豐富結果,模式仍然幫助 LLM 理解你的內容。將結構化數據視為通用機器可讀層,而不僅僅是 Google 功能。