React Virtualization for Large Lists and Trees
Problem
Users experience UI lag and unresponsiveness when:
Opening large documents — A document with 100+ nested blocks/sections causes slow initial render and janky scrolling as React mounts the entire tree
Viewing discussion feeds — Long lists of comments/discussions (50+) bog down the UI, especially on lower-powered devices
Browsing document lists — Feeds showing many document cards (homepage, search results) become sluggish
The source data is fine to keep in memory. The bottleneck is React's reconciliation + DOM mounting — we're rendering hundreds of components that aren't even visible.
Solution
Implement windowing/virtualization to render only visible items. Two libraries depending on data structure.
We might choose to use one or both of these libraries. It is undecided which is best, so we should experiment with both or even more.
Our documents have existing expand/collapse behavior which we would need to rewrite.
Pre-Solution
We should ensure that we are using clever memoization when rendering docs and long lists. This is an easier fix that would mitigate the demand for this project.
Flat Lists → @tanstack/react-virtual
For discussions, feeds, and simple document lists:
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => containerRef.current,
estimateSize: () => 80, // estimated row height
})
// Only render virtualizer.getVirtualItems() — typically 10-20 itemsOpen question: How do we handle variable-height items? TanStack supports measureElement for dynamic sizing but adds complexity.
Hierarchical Data → react-arborist
For document outline/block trees:
<Tree
data={documentBlocks}
width={containerWidth}
height={containerHeight}
rowHeight={32}
>
{({ node, style }) => <BlockRenderer node={node} style={style} />}
</Tree>Handles expand/collapse and only renders visible nodes automatically.
Open question: How does this integrate with our current recursive block rendering? We'd need to flatten our tree model or adapt our renderers to arborist's API.
Integration Points
DiscussionList — flat virtualization
DocumentOutline / sidebar tree — arborist
BlockEditor content area — needs investigation (may conflict with editor state)
FeedView (home, search results) — flat virtualization
Scope
Frontend only — no backend changes
Medium effort — 1-2 weeks depending on how deeply embedded current rendering is
Affects: packages/app, possibly packages/editor
No design changes required (virtualization is invisible to users when done right)
Rabbit Holes
Editor integration — Does virtualizing blocks break selection, cursor position, or collaborative editing state?
Scroll restoration — Virtualized lists lose scroll position on re-mount; need to persist/restore
Dynamic heights — If block heights vary significantly, measurement overhead may negate perf gains
Animations — Current expand/collapse animations may not play nicely with virtualization
No Goes
Pagination/lazy loading from backend — This project is about render performance, not data fetching
Rewriting the editor — We virtualize around the editor, not inside it (for now)
Mobile-specific optimizations — Same solution should work everywhere; no platform branching
Server-side rendering changes — Virtualization is client-only concern