JSON viewer

A collapsible JSON tree with search, paging for long arrays, and copy value or path.

datanew
Preview
JSON viewer preview
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 keys

When 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
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 keys

Keyboard interactions

KeysAction
ArrowDown / ArrowUpMoves to the next or previous visible row.
ArrowRightOpens a closed branch, or moves into an open one.
ArrowLeftCloses an open branch, or moves to the parent.
Home / EndJumps to the first or last visible row.
Enter / SpaceToggles a branch, or loads the next page on a Show more row.
Cmd/Ctrl + CCopies 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.