Skip to content

feat: virtualized rendering for large JSON (#32)#80

Open
brycewc wants to merge 8 commits into
YYsuni:mainfrom
brycewc:virtualization-support
Open

feat: virtualized rendering for large JSON (#32)#80
brycewc wants to merge 8 commits into
YYsuni:mainfrom
brycewc:virtualization-support

Conversation

@brycewc

@brycewc brycewc commented Jul 20, 2026

Copy link
Copy Markdown

Closes #32.

Summary

Adds windowed (virtualized) rendering so large JSON documents stay fast to render and interact with. The tree is now built from a flattened list of visible rows, and large documents are windowed with @tanstack/react-virtual so only the rows in view are mounted — keeping rendering smooth on documents with tens of thousands of nodes.

What changed

  • Flattened row model. The recursive DOM is replaced by a flat list of .jv-row elements indented via padding-left. Core logic lives in src/core/flatten.ts, src/core/row.ts, src/core/path.ts, and src/core/expand-state.ts.
  • Windowing. src/components/virtual-list.tsx + src/components/row.tsx render the visible window via @tanstack/react-virtual.
  • scrollRef prop. Lets consumers own the scroll container instead of the package always owning it.

New props

Prop Type Default Description
virtual boolean | 'auto' 'auto' Windowed rendering. 'auto' windows only when visible-row count exceeds rowVirtualThreshold or a height/maxHeight is set.
rowVirtualThreshold integer 100 Under virtual='auto', the row count above which windowing turns on.
height number | string - Fixed height of the scroll viewport (enables windowing).
maxHeight number | string '70vh' when windowed Grow-to-content up to this height, then scroll.
estimatedRowHeight integer 20 Estimated row height (px) before measurement.
overscan integer 8 Rows rendered beyond the viewport on each side.

Behavior / compatibility notes

This is a breaking (major) change of the rendering internals (v0.3):

  • Bounded height by default for large data once windowed — pass virtual={false} to restore the old unbounded layout.
  • onCollapse semantics fixed — no longer fires on mount; isCollapsing is now true when collapsing (previously inverted); fires only on user toggles.
  • onEdit/onDelete/onAdd/onChange payloads (depth, parentPath, indexOrName, parentType) are unchanged.
  • Windowing is client-only; SSR renders small documents inline. Force virtual={false} to server-render very large documents.

Tests

Adds a Vitest setup with characterization tests (src/test/characterization/*) covering render, callbacks, and customization, plus src/test/virtualization.test.tsx and src/core/flatten.test.ts.

brycewc and others added 8 commits July 14, 2026 18:48
Replace the recursive-DOM renderer with a flatten -> window architecture so
documents with tens of thousands of nodes stay fast.

- src/core: pure flatten() turns src + a path-keyed expand store into an ordered
  list of visible rows (collapsed subtrees are never walked); reuses the existing
  isCollapsed helpers.
- @tanstack/react-virtual windows the rows, mounting only what's in view with
  dynamic measurement.
- Volatile state (collapse/edit/add/delete/long-string) lifted out of per-node
  useState into central path-keyed stores so it survives scroll unmount/remount.
- Split Config/Handlers contexts + React.memo row comparator so scrolling and
  edits only re-render changed rows.

New props (backward-compatible defaults): virtual ('auto'), rowVirtualThreshold,
height, maxHeight, estimatedRowHeight, overscan. Under 'auto', small documents
render inline as before; windowing engages past the threshold or when a height
is set.

Breaking (0.3.0):
- Large data renders in a bounded scroll box; opt out with virtual={false}.
- onCollapse no longer fires on mount and isCollapsing is no longer inverted.
onEdit/onDelete/onAdd/onChange payloads are unchanged.

Adds Vitest test infra (43 tests: flatten, callback/custom parity, windowing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the recursive-DOM renderer with a flatten -> window architecture so
documents with tens of thousands of nodes stay fast.

- src/core: pure flatten() turns src + a path-keyed expand store into an ordered
  list of visible rows (collapsed subtrees are never walked); reuses the existing
  isCollapsed helpers.
- @tanstack/react-virtual windows the rows, mounting only what's in view with
  dynamic measurement.
- Volatile state (collapse/edit/add/delete/long-string) lifted out of per-node
  useState into central path-keyed stores so it survives scroll unmount/remount.
- Split Config/Handlers contexts + React.memo row comparator so scrolling and
  edits only re-render changed rows.

New props (backward-compatible defaults): virtual ('auto'), rowVirtualThreshold,
height, maxHeight, estimatedRowHeight, overscan. Under 'auto', small documents
render inline as before; windowing engages past the threshold or when a height
is set.

Breaking (0.3.0):
- Large data renders in a bounded scroll box; opt out with virtual={false}.
- onCollapse no longer fires on mount and isCollapsing is no longer inverted.
onEdit/onDelete/onAdd/onChange payloads are unchanged.

Adds Vitest test infra (43 tests: flatten, callback/custom parity, windowing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

@brycewc is attempting to deploy a commit to the Suni's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Visualization / Performance Boost

1 participant