Skip to content

FRD-066: JSON Viewer Widget

FieldValue
OwnerDavid Holmes
StatusDraft
Last Updated2026-05-26
Target Releasev2.0.0 (P2)
T-Shirt SizeM
TypeWidget

Document Summary

A structured JSON viewer widget with a collapsible tree, syntax highlighting, copy-to-clipboard, and search. Designed for API response inspection, webhook payload debugging, and configuration review. Complements the existing CodeViewer (Monaco-based) with a lighter-weight, tree-oriented alternative.


Introduction

Overview

Developers and operators frequently need to inspect JSON data in SaaS dashboards, API explorers, and devtools panels. The existing CodeViewer uses Monaco Editor for full-featured code editing but is heavyweight for read-only JSON inspection. This widget provides a purpose-built, lightweight JSON tree viewer with collapse, search, and copy.

Goals

  • Parse and render JSON as a collapsible, syntax-highlighted tree.
  • Support expand/collapse at any node level, with expand-all/collapse-all controls.
  • Copy the full JSON or a subtree to the clipboard.
  • Search/filter within the JSON by key or value.
  • Ship Storybook stories with various JSON structures.

Non-Goals

  • JSON editing or mutation (use CodeViewer for editing).
  • Schema validation or JSON Schema rendering.
  • Binary/non-JSON data display.
  • Diffing two JSON documents.

Scope

In Scope

ItemDescription
JsonViewer componentCollapsible tree with syntax highlighting
Collapse controlsPer-node toggle; expand-all / collapse-all buttons
Copy supportCopy full document or individual subtrees
SearchText search highlighting matching keys and values
Syntax highlightingColor-coded keys, strings, numbers, booleans, nulls
Storybook storiesSimpleObject, DeepNested, LargeArray, WithSearch, EmptyObject, InvalidJSON

Out of Scope

ItemRationale
EditingCodeViewer handles editing; this widget is read-only
JSON SchemaSeparate concern; would add significant complexity
DiffingSeparate widget for JSON diff views
YAML/TOML supportJSON-only; other formats are separate widgets

Users and Pain Points

UserPain Point
DevelopersCodeViewer is too heavy for simple JSON inspection in dashboards
SRE/DevOps teamsWebhook payloads and API responses are hard to read as raw text
Support engineersDifficulty navigating deeply nested configuration objects

Definitions

TermDefinition
JSON treeA hierarchical representation of a JSON document with expandable nodes
NodeA key-value pair (object) or indexed element (array) in the tree
Syntax highlightingColor-coding of data types (string, number, boolean, null)

Current State

code-viewer.tsx provides a Monaco Editor-based code viewer with syntax highlighting, section navigation, and copy/download. manifest-viewer.tsx re-exports CodeViewer. Both are full-featured but heavy (~hundreds of KB due to Monaco). No lightweight tree-based JSON viewer exists for simple inspection use cases.


Proposed Solution

Create a JsonViewer widget at src/components/widgets/json-viewer.tsx that:

  1. Accepts a data prop (parsed object/array) or a raw string prop.
  2. Renders a recursive tree of collapsible nodes.
  3. Syntax-highlights keys, strings, numbers, booleans, and null values using design tokens.
  4. Provides expand-all and collapse-all controls in a toolbar.
  5. Supports copying the full JSON or any subtree via a context action.
  6. Includes a search input that highlights matching keys and values and filters the tree.
  7. Uses no external editor dependencies (pure React rendering).

Requirements

The viewer must handle JSON documents up to 1 MB without freezing the UI. Large arrays should virtualize or paginate. Invalid JSON input should show an error state, not crash.


Functional Requirements

IDRequirementPriority
FR-01Parse and render a JSON object or array as a collapsible treeMust
FR-02Toggle expand/collapse on any node by clicking the node handleMust
FR-03Provide expand-all and collapse-all toolbar buttonsMust
FR-04Syntax-highlight keys (blue), strings (green), numbers (purple), booleans (orange), null (gray)Must
FR-05Display array indices and object key counts on collapsed nodesMust
FR-06Copy full JSON to clipboard via toolbar buttonMust
FR-07Copy a subtree by right-click or icon on a specific nodeShould
FR-08Search input filters/highlights keys and values matching the queryMust
FR-09Accept either a parsed data prop or a raw string propMust
FR-10Show an error state for invalid JSON when using raw propMust
FR-11Support defaultExpandDepth to control initial expansion levelShould
FR-12Virtualize or paginate arrays with more than 100 elementsShould

Non-Functional Requirements

IDRequirement
NFR-01Bundle size under 6 KB gzipped (no Monaco or external editors)
NFR-02Render 1000-key objects without perceptible jank
NFR-03Full light/dark theme support with appropriate token colors
NFR-04Search updates within 100 ms of keystroke

API / Interface Requirements

interface JsonViewerProps {
data?: unknown; // parsed JSON (object, array, primitive)
raw?: string; // raw JSON string (parsed internally)
defaultExpandDepth?: number; // default 2
showToolbar?: boolean; // default true
showSearch?: boolean; // default true
showLineNumbers?: boolean; // default false
copyable?: boolean; // default true
maxHeight?: number | string;
emptyLabel?: string; // default "Empty"
errorLabel?: string; // default "Invalid JSON"
className?: string;
}

Accessibility Requirements

IDRequirement
A11Y-01Tree uses role="tree" with role="treeitem" per node
A11Y-02Expand/collapse toggled via Enter/Space on focused nodes
A11Y-03Arrow keys navigate the tree (Up/Down between siblings, Left to collapse, Right to expand)
A11Y-04Search input has a visible label or aria-label
A11Y-05Copy confirmation announced via aria-live="polite"
A11Y-06Error state for invalid JSON uses role="alert"

Content and Documentation Requirements

  • Storybook doc page comparing JsonViewer vs. CodeViewer for JSON use cases.
  • Stories: SimpleObject, DeepNested, LargeArray, WithSearch, EmptyObject, InvalidJSON, DarkMode.
  • JSDoc on all exported types.
  • Migration guidance for teams currently using CodeViewer for read-only JSON.

Dependencies

DependencyTypeNotes
ButtonInternalToolbar actions
InputInternalSearch field
Clipboard APIBrowserCopy support
Design tokensInternalSyntax-highlight colors

Risks and Tradeoffs

RiskImpactMitigation
Very large JSON freezes UIPoor performanceVirtualize large arrays; lazy-render deep subtrees
Custom tree rendering vs. MonacoFeature gap for power usersPosition as complementary to CodeViewer, not replacement
Circular references in dataStack overflowDetect and display “[Circular]” placeholder

Open Questions

  1. Should the viewer support JSON path display (e.g., $.data.items[0].name) on hover?
  2. Do we need a raw/formatted toggle, or is formatted-only sufficient?
  3. Should search support JSONPath or regex patterns, or plain text only?

Acceptance Criteria

  • Renders a JSON object and array as a collapsible tree.
  • Expand/collapse works at any node level.
  • Syntax highlighting applies correct colors per data type.
  • Expand-all and collapse-all toolbar buttons work.
  • Copy-to-clipboard works for the full document.
  • Search highlights matching keys and values.
  • Invalid JSON input shows error state.
  • Tree is keyboard-navigable per WAI-ARIA tree pattern.
  • All Storybook stories render without errors.
  • Passes axe accessibility audit with zero violations.
  • Unit tests cover expand/collapse, search, copy, invalid JSON, and empty states.

LLM Handoff Instructions

When implementing this FRD:

  1. Create src/components/widgets/json-viewer.tsx.
  2. Build a recursive JsonNode component for tree rendering. Do NOT use Monaco or any external editor.
  3. Use <details>/<summary> or custom toggle for collapse behavior; add role="tree" / role="treeitem" ARIA attributes.
  4. Create src/components/widgets/json-viewer.stories.tsx with all listed stories.
  5. Create src/components/widgets/json-viewer.test.tsx.
  6. Handle raw prop parsing with try/catch; show error state on failure.
  7. Use navigator.clipboard.writeText with fallback for copy.
  8. Reference code-viewer.tsx for toolbar patterns (copy, section nav) but do not import Monaco.

Decision Log

DateDecisionRationale
2026-05-26Pure React rendering, no MonacoKeeps bundle small; Monaco is overkill for read-only tree views
2026-05-26Accept both parsed data and raw stringFlexibility for consumers who have either form

Document History

DateVersionAuthorChanges
2026-05-260.1David HolmesInitial draft