State Inspector

Compact JSON-style tree viewer for any JavaScript value. Renders strings, numbers, booleans, null/undefined, dates, regexes, functions, arrays, and objects — each with its own colour — and lets you click to expand or collapse any object or array node. Designed for "what's in this state right now" debug panels in lesson-style interactive demos.

"id": 42,
"name": "Ada Lovelace",
"admin": true,
"lastSeen": null
},
0: 98,
1: 87,
2: 91,
3: 100
],
"beta": true,
"experimental": false
},
"notes": "first run"
}
Customize
Value
nested
Depth
4

Installation

npx shadcn@latest add https://craftbits.dev/r/state-inspector.json

Usage

import { StateInspector } from "@craft-bits/core";
 
<StateInspector value={{ count: 1, items: ["a", "b"], active: true }} />

Understanding the component

  1. Recursive renderer. value is narrowed to one of a dozen ValueKinds — string, number, boolean, null, undefined, date, regexp, function, symbol, array, object, circular — by typeof, Array.isArray, and instanceof checks. Each kind has its own renderer and colour.
  2. Expand / collapse. Object and array nodes render as a <button> row with a chevron. Clicking toggles the children's container between hidden and block. The chevron itself uses SPRINGS.snap for a 0°→90° rotation — quick, no overshoot.
  3. Cycle detection. Each recursion passes a ReadonlySet<object> of ancestors. When a node's value is already in that set, the kind becomes "circular" and it renders as [Circular] instead of recursing forever — try the circular sample above.
  4. Depth-aware default. defaultExpanded only opens nodes whose depth is below maxDepth. A deeply-nested blob therefore renders fast — deep levels stay collapsed until clicked.
  5. Monospace + tabular nums. The whole tree uses font-cb-mono and tabular-nums for array indices so numeric columns align.

Props

PropTypeDefaultDescription
valueunknownrequiredAny JavaScript value. Objects and arrays render as expandable trees.
defaultExpandedbooleantrueWhether the root object/array starts expanded.
maxDepthnumber4Maximum nesting depth that auto-expands; deeper nodes stay collapsed until clicked.
wrapAtnumber60Single-line collapsed previews wrap when they would exceed this width.
classNamestringMerged onto the root <div>.

Accessibility

  • The root element has role="tree" so assistive tech recognises the structure.
  • Each expandable row is a <button> with aria-expanded={open}.
  • Buttons are reachable by Tab and toggle with Enter/Space (native button semantics).
  • Focus ring uses --cb-accent via focus-visible:outline.
  • Animations are limited to the chevron's rotation; the disclosure itself is an instant DOM toggle, so users with prefers-reduced-motion aren't blocked from interacting.

Credits

  • Extracted from: terminal-dreams (src/components/recipe-lab/StateInspector.tsx). The library version is a re-architecture — the source rendered a flat label/value list, the library version is a fully recursive JSON tree with expand/collapse, cycle detection, and a wider set of value kinds.