JSON viewer
A collapsible JSON tree with search, paging for long arrays, and copy value or path.
datanew
Preview
JSON viewer preview
Live · keyboard ready
root8 keys
id"a1b2c3"
name"Ada Lovelace"
role"Staff Engineer"
teams2 items
0"Analytics"
1"Platform"
plan3 keys
tier"scale"
seats24
renews"2026-12-01"
flags2 keys
betatrue
legacyfalse
logins3 items
01700000000000
11700003600000
21700007200000
metanull
rootobject, 8 keysWhen to use
- Inspecting a JSON payload in a developer dashboard, such as webhooks or API logs.
- Large nested objects where search and copy path save time.
- Arrays with hundreds of items, which load in pages of pageSize.
When not to use
- Use code-block to show JSON as formatted source for copying whole.
- Use tree-view for folders, navigation, or other non-JSON hierarchies.
- Use data-grid when the data is a flat list of records.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "json-viewer",
"type": "registry:ui",
"title": "JSON viewer",
"description": "A collapsible JSON tree with search, paging for long arrays, and copy value or path.",
"categories": [
"data"
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/json-viewer/json-viewer.tsx",
"type": "registry:component",
"target": "@components/manicat/json-viewer/json-viewer.tsx"
},
{
"path": "registry/components/json-viewer/json-viewer.module.css",
"type": "registry:component",
"target": "@components/manicat/json-viewer/json-viewer.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/json-viewer",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/json-viewer",
"markdown": "https://uiarc.dev/components/json-viewer/markdown",
"tags": [
"data",
"new"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { JsonViewer } from "@/components/manicat/json-viewer/json-viewer";
export function JsonViewerExample() {
return (
<JsonViewer
label="Account payload"
defaultExpandDepth={2}
data={{
id: "a1b2c3",
name: "Ada Lovelace",
role: "Staff Engineer",
teams: ["Analytics", "Platform"],
plan: { tier: "scale", seats: 24, renews: "2026-12-01" },
flags: { beta: true, legacy: false },
logins: [1700000000000, 1700003600000, 1700007200000],
meta: null,
}}
/>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
API response A collapsible tree of a payload, with types coloured and long arrays folded.
API response
A collapsible tree of a payload, with types coloured and long arrays folded. preview
Live · keyboard ready
root7 keys
id"a1b2c3"
name"Ada Lovelace"
role"Staff Engineer"
teams2 items
0"Analytics"
1"Platform"
plan3 keys
tier"scale"
seats24
renews"2026-12-01"
flags2 keys
betatrue
legacyfalse
metanull
rootobject, 7 keysapi-response-a-collapsible-tree-of-a-payload-with-types-coloured-and-long-arrays-folded-.tsxtsx
"use client";
import { JsonViewer } from "@/components/manicat/json-viewer/json-viewer";
export function JsonViewerResponseExample() {
return (
<JsonViewer
label="GET /v1/accounts/a1b2c3"
defaultExpandDepth={2}
data={{
id: "a1b2c3",
name: "Ada Lovelace",
role: "Staff Engineer",
teams: ["Analytics", "Platform"],
plan: { tier: "scale", seats: 24, renews: "2026-12-01" },
flags: { beta: true, legacy: false },
meta: null,
}}
/>
);
}
Keyboard interactions
| Keys | Action |
|---|---|
| ArrowDown / ArrowUp | Moves to the next or previous visible row. |
| ArrowRight | Opens a closed branch, or moves into an open one. |
| ArrowLeft | Closes an open branch, or moves to the parent. |
| Home / End | Jumps to the first or last visible row. |
| Enter / Space | Toggles a branch, or loads the next page on a Show more row. |
| Cmd/Ctrl + C | Copies the focused row's value; add Shift to copy its path. Ignored while text is selected. |
| / | Focuses the search field. |
| Enter / Shift + Enter (in search) | Jumps to the next or previous match. |
| Escape (in search) | Clears the query. |
| ArrowDown (in search) | Moves focus into the tree. |
Accessibility
- Follows the WAI-ARIA tree pattern: role="tree" with treeitems carrying aria-level, aria-posinset, aria-setsize, aria-expanded, and aria-selected.
- One row is tabbable at a time; each row's label reads its key and value, or its type and item count.
- The match counter is aria-live polite, and a status region announces copies and failures.
- Row copy buttons are out of the tab order; use Cmd or Ctrl with C, or the path bar's copy button, from the keyboard.
Motion
- Each row unfolds its own height, so opening a branch, loading a page, or expanding all share one motion.
- The current-row highlight glides between rows with a shared layout id, and the tree scroll springs to where the row will sit once rows settle.
- Copy glyphs swap to a check or cross with a small scale and blur.
- Reduced motion, or more than 400 visible rows, makes row changes instant; reduced motion also jumps the scroll and highlight.
Responsive behavior
- The tree scrolls inside maxHeight; long keys and values truncate with an ellipsis and strings over 40 characters show in full on hover.
- At 420px viewport width and below the indent drops from 16px to 12px, keys cap at 40% of the row, and the path type hides.
- Row copy actions show on hover with a fine pointer; on touch they show on the current row.
Performance
- Rows are a flat list rebuilt only when data, open branches, pages, or search change; nothing is virtualized, so paging keeps the DOM small.
- Above 400 visible rows the height animations switch off to keep expand all fast.
- Search walks the whole value on each keystroke; for multi-megabyte payloads control query and debounce it.
Notes for AI
- Use for API responses, webhook payloads, logs, and config inspection in developer tools.
- Search matches keys and primitive values case-insensitively, opens the branches that hold them, and stops at 2000 matches.
- Control expanded to persist open branches across reloads, or to open a path from elsewhere in the page.
- For formatted source code use code-block; for tabular data use data-grid.