Skip to content

Components

A React JSON Tree Viewer That Survives Big Payloads — Render the Open Nodes, Count the Rest

Lazy Children, Collapsed-by-Default Depth, and a Copy Path That Developers Actually Want
Written by Aiden Wood Founder and Lead Design Engineer
🌳 🔬

Every admin tool eventually needs to show raw JSON — a webhook payload, an API response, an audit log. JSON.stringify(data, null, 2) in a <pre> works until the payload is 2MB of nested arrays, and the "proper" tree viewers often make it worse by mounting a React component for every node in the document up front. Ten thousand nodes, ten thousand components, one frozen tab.

Velocity X's standard-json-tree-viewer is built on the only rule that scales: render what's expanded, summarise what isn't.

The Money Pattern

A collapsed node renders one row — its key, its type, and a count — regardless of what's inside. Children don't exist in the React tree until expansion:

function Node({ k, value, depth }) {
  const [open, setOpen] = useState(depth < 2); // auto-open shallow levels
  const isObj = value !== null && typeof value === 'object';

  if (!isObj) return <Leaf k={k} value={value} />;

  const size = Array.isArray(value) ? value.length : Object.keys(value).length;
  return (
    <div>
      <button aria-expanded={open} onClick={() => setOpen(o => !o)}>
        {k}: {Array.isArray(value) ? `[${size}]` : `{${size}}`}
      </button>
      {open && Object.entries(value).map(([ck, cv]) =>
        <Node key={ck} k={ck} value={cv} depth={depth + 1} />
      )}
    </div>
  );
}

The open && is the entire performance model: a 2MB payload with everything collapsed is a few dozen components, not ten thousand. Depth-based auto-open (levels 0–1) gives orientation without the mount cost.

The Catch

The feature that separates a viewer people tolerate from one they like: copy path. Right-clicking a node should hand you payload.items[3].customer.email — because the next thing anyone does with a JSON viewer is write code against the shape they just found. Copy-value matters too, but copy-path is the one nobody ships.

Watch the types at the edges: null vs undefined vs "null" (string) must render distinctly, big numbers past MAX_SAFE_INTEGER arrive already mangled if you parsed with JSON.parse — if your IDs are int64, keep them as strings server-side.

The Verdict

A JSON viewer is a lazy tree plus two copy buttons, and the lazy part is non-negotiable at real payload sizes. The finished one lives in Velocity X's Standard Data category next to the sortable table and the inline-editable grid — application surfaces, source you own.

Let us make some quick suggestions?

Please provide your full name.
Please provide your phone number.
Please provide a valid phone number.
Please provide your email address.
Please provide a valid email address.
Please provide your brand name or website.
Please provide your brand name or website.