JSON 樹檢視器
可收合的 JSON 樹:型別配色、路徑複製、展開動畫。
以等寬字型遞迴渲染任何 JSON 資料:字串綠、數字藍、布林橘、null 紅,物件與陣列可展開收合(高度動畫加箭頭旋轉,並顯示項數摘要),滑過任一列即可一鍵複製該節點的路徑或值,適合 API 除錯面板與開發者工具。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add json-tree-viewerJSON Tree Viewer 是用 React、Tailwind CSS 與 Motion 做的 JSON 檢視器(JSON viewer)元件,圖示來自 lucide-react。傳入一份 JsonValue 資料就會渲染成可收合的樹狀結構,每一列都能複製該節點的存取路徑(例如 $.user.tags[0])或格式化後的值。
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
<JsonTreeViewer/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
適用情境
- API 除錯面板或後台的「原始回應」分頁,讓開發者展開查看巢狀欄位。
- Webhook、事件紀錄或稽核日誌的 payload 檢視。
- 設定檔、feature flag 或 CMS 欄位的唯讀預覽。
- 開發者文件中展示範例回應,讀者可以直接複製欄位路徑。
- 不適合:要呈現的是資料夾與檔案結構時,改用 File Tree。
安裝
npx webberui@latest add https://webberui.com/r/json-tree-viewer.json或在 components.json 設定 registries 後,改用 @webberui/json-tree-viewer 安裝。
使用
import { JsonTreeViewer } from "@/components/ui/json-tree-viewer";
<JsonTreeViewer
data={{ name: "Webber", stars: 1024, tags: ["ui", "react"], beta: false }}
defaultExpandDepth={2}
rootName="$"
/>範例
從 API 載入資料
在 Client Component 中取得資料後傳給 data;載入完成前可以先顯示一段提示文字。
"use client";
import * as React from "react";
import { JsonTreeViewer, type JsonValue } from "@/components/ui/json-tree-viewer";
export function ResponsePanel({ url }: { url: string }) {
const [data, setData] = React.useState<JsonValue | undefined>(undefined);
React.useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((json: JsonValue) => setData(json));
}, [url]);
if (data === undefined) return <p className="text-sm text-neutral-500">載入中…</p>;
return <JsonTreeViewer data={data} rootName="response" />;
}大型資料預設收合並限制高度
資料層級很深時,把 defaultExpandDepth 設成 1 只展開第一層;外層用 className 限制高度並加上捲軸。
<JsonTreeViewer
data={orderPayload}
defaultExpandDepth={1}
rootName="order"
className="max-h-96 overflow-auto rounded-lg border p-3"
/>在 Server Component 頁面直接傳入
JsonValue 只包含可序列化的型別,伺服器端取得的資料可以直接當 props 傳給這個 Client Component。
// app/admin/webhooks/[id]/page.tsx
import { JsonTreeViewer } from "@/components/ui/json-tree-viewer";
import { getWebhookEvent } from "@/lib/webhooks";
export default async function WebhookEventPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const event = await getWebhookEvent(id);
return (
<section className="space-y-3">
<h1 className="text-lg font-semibold">事件 {id}</h1>
<JsonTreeViewer data={event.payload} rootName="payload" defaultExpandDepth={Infinity} />
</section>
);
}Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
data | JsonValue | — | 要渲染的 JSON 資料(物件、陣列或任何原始值) |
defaultExpandDepth | number | 2 | 預設展開的層數,0 全部收合、Infinity 全部展開 |
rootName | string | "$" | 複製路徑時的根名稱,例如 $ 或 data |
className | string | — | 透傳到最外層容器,與內建樣式合併 |
可及性
- 展開/收合為原生
button,附aria-expanded與含節點路徑的中文aria-label,鍵盤 Enter 與 Space 皆可操作 - 「複製路徑/複製值」按鈕在鍵盤聚焦(focus-visible)時同樣會顯現,不依賴滑鼠 hover
- 複製成功以圖示切換為打勾提供視覺回饋,且按鈕保持可見 1.4 秒
- 使用者系統開啟「減少動態效果」時,展開收合與箭頭旋轉改為即時切換,不播放高度動畫
常見問題
可以在 Next.js App Router 使用嗎?
可以。元件檔案開頭已有 "use client",在 Server Component 頁面直接 import 並傳入資料即可。剪貼簿與計時器只在使用者點擊複製時才會用到 navigator 與 window,不影響伺服器渲染。
複製出來的路徑是什麼格式?
根節點使用 rootName(預設 $),合法的 JavaScript 識別字用點記法,陣列索引與含中文、連字號等字元的 key 改用中括號,例如 $.items[0]["shipping-address"]。「值」按鈕複製的是縮排 2 格的 JSON.stringify 結果。
改了 defaultExpandDepth 卻沒有反應?
每個節點的展開狀態只在第一次掛載時依 defaultExpandDepth 決定,之後由使用者自行展開收合。需要整棵樹重新套用時,替 JsonTreeViewer 換一個新的 key 讓它重新掛載。
資料很大會不會很慢?
收合中的節點不會渲染子節點,但元件沒有做虛擬捲動,展開的部分都會留在 DOM 中。資料量大時建議把 defaultExpandDepth 設低,並用 className 限制高度。
為什麼複製沒有成功?
元件使用 navigator.clipboard.writeText,瀏覽器只在 HTTPS 或 localhost 等安全環境提供這個 API;寫入失敗時錯誤會被忽略,但打勾圖示仍會顯示。依賴為 motion、lucide-react、clsx 與 tailwind-merge。