Tenten AIGEO
回到部落格
技術實作手冊實作

在 Next.js App Router 注入 JSON-LD:用 Server Component 實作結構化資料

在 Next.js App Router 中,於 Server Component 直接輸出 `<script type="application/ld+json">`,讓結構化資料進入初始 HTML,AI 爬蟲不必執行 JavaScript 就能讀取。內含最小 page.tsx 範例、@graph 多實體串接與上線前的 curl 驗證步驟。

Tenten GEO 團隊發布於 2026-07-125 分鐘閱讀
示意圖:伺服器端輸出的結構化資料,直接流入 AI 爬蟲讀取的初始 HTML。

在 App Router 注入 JSON-LD,最可靠的做法是在 Server Component 裡直接輸出一段 `<script type="application/ld+json">`。這樣結構化資料會出現在伺服器回傳的初始 HTML,AI 爬蟲不必執行 JavaScript 就能讀到。用 useEffect 或 Google Tag Manager 事後塞進去的 JSON-LD,對答案引擎幾乎等於不存在——它們大多不會等你的頁面 hydrate 完成,就已經抓完離開。

為什麼一定要在伺服器端輸出

差別在於時機。Server Component 在請求當下就把 JSON-LD 寫進 HTML 字串,GPTBot、ClaudeBot、PerplexityBot、Google-Extended 這類爬蟲抓到的第一份文件就含有完整 schema。客戶端注入則要等瀏覽器下載並執行你的 bundle,多數 AI 爬蟲根本不跑這一步。這裡有個容易誤判的陷阱:Google 的 Rich Results Test 會替你渲染 JavaScript,所以客戶端注入的資料在測試工具裡看起來一切正常,你以為過關了;但實際被答案引擎讀取時,它們拿到的是沒經過渲染的原始 HTML,裡面什麼 schema 都沒有。我們替客戶做技術審計時,這正是最常見、也最容易被忽略的失分項。

最小可行範例:一頁 page.tsx

實作本身很短。你不需要任何額外套件,也不需要 API route,就在渲染文章的那個 Server Component 內完成。核心只有三件事:取資料、組物件、輸出 script。

  1. 在 `app/blog/[slug]/page.tsx`(預設就是 Server Component)內,先 `await` 取得這篇文章的資料。
  2. 組一個純 JavaScript 物件 `jsonLd`,開頭放 `"@context": "https://schema.org"` 與 `"@type": "Article"`,其餘欄位(headline、datePublished、author、image)用文章資料動態填入。
  3. 在回傳的 JSX 最前面放:`<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c") }} />`。
  4. 不要 import 任何客戶端函式庫,也不要在檔案頂端加 `"use client"`——一旦加了,它就變成客戶端元件,前面所有努力歸零。

用 @graph 串起多個實體

單一頁面通常需要不只一種 schema:品牌本身是 Organization,這篇文章是 Article,導覽路徑是 BreadcrumbList,問答區塊是 FAQPage。與其輸出四個各自獨立的 script 標籤,更乾淨的做法是用一個 `@graph` 陣列把它們裝進同一個 JSON-LD 物件,再用 `@id` 互相指向——例如讓 Article 的 `publisher` 指到 Organization 的 `@id`,讓 BreadcrumbList 的最後一項指到當頁 `@id`。答案引擎解析一次就拿到完整的實體關係圖,也比較不會抓到彼此矛盾或重複的節點。

  • Organization 與 WebSite:放在 `app/layout.tsx`,全站共用一次即可,不要每頁重複宣告。
  • Article 或 BlogPosting:放在文章的 `page.tsx`,欄位用當頁資料動態填。
  • BreadcrumbList:依路由層級組出,對 AI 理解站內結構特別有幫助。
  • FAQPage:只在畫面真的有問答區塊時才加,且 `acceptedAnswer` 的文字要與畫面內容一致,別為了 schema 而造假。
資訊圖:Server Component 於初始 HTML 輸出 JSON-LD,客戶端注入則被 AI 爬蟲略過。
伺服器端輸出的 JSON-LD 進入初始 HTML,客戶端注入的則等不到爬蟲。

讓 JSON-LD 可維護

把 schema 物件散落在各個頁面,三個月後沒人敢改。我們的做法是抽一個 `lib/schema.ts`,用 `schema-dts` 型別(`import type { Article, WithContext } from "schema-dts"`)約束每個產生器函式的回傳值,欄位名稱打錯或型別不對,TypeScript 在編譯階段就會報錯。頁面只呼叫 `buildArticleSchema(post)`,拿到型別安全的物件再輸出,改一次全站生效。網址一律用絕對路徑,並集中在一個 `SITE_URL` 常數,避免 `@id` 在正式站與 preview 網域之間對不上。

常見錯誤與上線前驗證

  • JSON-LD 內的 `url`、`image`、`datePublished` 與畫面實際內容不一致——答案引擎偵測到落差會降低對整頁的信任。
  • 用相對路徑當 `@id` 或 `url`,一換到 preview 網域就全部指向錯誤位置。
  • 忘了跳脫 `<`,或不小心在頁面頂端加了 `"use client"`,讓 script 變成客戶端輸出。
  • 上線前先用 Google Rich Results Test 檢查語法,再用 `curl` 直接抓頁面原始 HTML,確認 script 真的在初始回應裡,而不是只出現在瀏覽器 DevTools。這一步最關鍵,因為它模擬的正是 AI 爬蟲看到的畫面。
我們審計過的網站裡,多數 JSON-LD 問題不在 schema 寫錯,而在它只活在客戶端。伺服器回傳的 HTML 裡沒有,AI 就當它不存在。Tenten GEO 技術審計

從實作到被引用

把 JSON-LD 搬到 Server Component,是讓內容具備「可被機器讀取」的最低門檻。接下來還要對齊實體命名、內部連結與跨頁一致性,AI 才會穩定地把你當成可信來源引用。如果你不確定自己站上的結構化資料到底有沒有進到爬蟲看到的那份 HTML,可以預約 30 分鐘 GEO 診斷,我們用實際抓取的原始碼幫你盤點缺口,而不是只看渲染後的畫面。

常見問題

Next.js App Router 要怎麼加 JSON-LD?
在 Server Component(page.tsx 或 layout.tsx)內組一個 schema 物件,用 `<script type="application/ld+json">` 搭配 `dangerouslySetInnerHTML` 輸出,並把字串裡的 `<` 跳脫成 `<`。這樣結構化資料會進入初始 HTML,AI 爬蟲不必執行 JavaScript 就能讀到。
可以用 next/head 或 Metadata API 放 JSON-LD 嗎?
App Router 已經沒有 next/head,Metadata API 也不支援輸出任意 script 標籤。Next.js 官方建議直接在 Server Component 的 JSX 裡渲染 `<script type="application/ld+json">`,這是目前放 JSON-LD 最穩定、也最容易被爬蟲讀到的方式。
為什麼不建議用 GTM 或 useEffect 注入結構化資料?
這兩種都在客戶端執行,JSON-LD 要等瀏覽器 hydrate 後才出現。多數 AI 爬蟲只抓初始 HTML、不跑 JavaScript,等於看不到你的 schema。改由伺服器端輸出,才能確保結構化資料實際被答案引擎讀取與引用。

READY WHEN YOU ARE

想知道你的品牌在 AI 答案裡值多少?

30 分鐘 GEO 診斷,我們用你的實際提問,帶你看見在六大 AI 引擎裡的能見度缺口,以及補上它的優先順序。

預約 30 分鐘診斷