CONCEPT Cited by 1 source
Window virtualization¶
Window virtualization (a.k.a. virtual scrolling / virtual lists) is a front-end rendering technique that keeps only the visible portion of a long list or grid in the DOM at any moment. As the user scrolls, off-screen items are unmounted (or never mounted) and new items are swapped in — the browser sees a constant-size DOM instead of a list-length-proportional one. See the background reference at patterns.dev/vanilla/virtual-lists.
Why it's needed¶
Rendering a naive list of N items costs O(N) in:
- DOM nodes (memory + every browser-paint operation scales with node count).
- JS heap (each component's state, handlers, closures).
- Layout work (any layout pass traverses the whole tree).
- Event handler count (if attached per-item).
At tens of thousands of items these costs cross product-unusable thresholds. GitHub's Files-changed tab pre-virtualization on a p95+ PR showed >1 GB JS heap, >400,000 DOM nodes, and INP in the 275-700+ ms range.
Mechanism¶
- Scrollable viewport with a spacer element representing the full logical list height (so the scrollbar reflects true length).
- Measured or uniform item heights — library knows what range of items maps to the current scroll position.
- Render only that visible slice plus a small overscan buffer (typically a few screens) to hide mount/unmount flicker.
- On scroll: compute new slice, mount new items, unmount items now off-screen.
The DOM cost becomes O(viewport-size), independent of list length.
Trade-offs¶
- Native browser find-in-page sacrificed.
Ctrl-Fsearches the DOM; content not in the DOM is invisible to it. GitHub's strategy explicitly accepts this on p95+ PRs and preserves it on median PRs (per-size-tier choice). - Accessibility risks. Screen readers sometimes read list structure differently when items are virtualized; ARIA relationships need explicit handling.
- Scroll-position restoration across navigation is harder when items aren't persistently in the DOM.
- Fixed vs dynamic item sizes. Dynamic sizes (diff lines with wrapping text, comments expanded inline) need measurement passes that cost extra work.
- Library footprint. Adds a virtualization substrate (TanStack
Virtual,
react-window,react-virtuoso, etc.) that needs maintenance.
Canonical wiki instance¶
GitHub's pull-request Files-changed tab uses TanStack Virtual for p95+ PRs (>10,000 diff lines). Reported impact: 10× reduction in JS heap usage + DOM nodes, INP 275-700+ ms → 40-80 ms. (Source: sources/2026-04-03-github-the-uphill-climb-of-making-diff-lines-performant)
Server-side analogue¶
patterns/server-hydrate-visible-only — hydrate only the visible portion on the server-side-rendering pass, mirroring the client virtualization choice at the SSR layer. GitHub does both.
Related¶
- concepts/dom-node-count — the primary cost virtualization attacks.
- concepts/javascript-heap-size — the secondary cost (per-item component state).
- concepts/interaction-to-next-paint — the metric that exposes non-virtualized-list cost most visibly.
- concepts/hot-path — per-item render is the hottest path of a list UI.
- systems/tanstack-virtual — canonical React virtualization library.
- systems/github-pull-requests — canonical wiki production instance.
- patterns/server-hydrate-visible-only — SSR-layer counterpart.