Virtualization and Windowing
Master virtual scrolling for high-performance lists and tables: when to use it, library trade-offs, variable-height challenges, and integration patterns.
Rendering thousands of DOM nodes in a single list or table is one of the fastest ways to degrade frontend performance. The browser struggles with layout, paint, and memory when you mount hundreds or thousands of elements. Virtualization—also called windowing—solves this by rendering only what the user can see. Here's what architects need to know to design and implement high-performance list UIs.
The Problem: DOM Overload Kills Performance
Why Large Lists Are Slow
Every DOM node incurs a cost: layout calculations, style recalculation, paint, and memory. A list of 10,000 items means 10,000+ nodes (depending on DOM structure), each participating in layout. Scrolling triggers continuous layout and paint work as the browser tries to keep up. The result: janky scrolling, delayed input, and high memory usage—especially on lower-end devices.
The Numbers That Matter
Benchmarks typically show a clear inflection point. Up to ~100–200 visible items, most devices handle direct rendering fine. Beyond 500–1000, performance degrades. At 5000+, many apps become unusable. Virtualization keeps the DOM node count in the hundreds regardless of data size.
How Virtual Scrolling Works
Render Only the Visible Window
Virtual scrolling maintains a "window" of visible items. You compute which indices are in view based on scroll position and container height, then render only those items—plus a small buffer above and below for smooth scrolling. As the user scrolls, the window shifts and you unmount items that leave the viewport and mount new ones.
The Buffer Zone
A buffer of 5–10 items above and below the visible area prevents blank gaps when the user scrolls quickly. Too small a buffer causes flicker; too large defeats the purpose. Most libraries expose an overscanCount or similar parameter.
import { FixedSizeList } from 'react-window';
// Only ~15–20 items in DOM at any time, regardless of data size
<FixedSizeList
height={600}
itemCount={10000}
itemSize={50}
width="100%"
overscanCount={5}
>
{({ index, style }) => (
Continue reading Virtualization and Windowing
Sign in or create a free account to read the rest of this article and all premium content.
Sign in to continue readingRelated articles
- Frontend ArchitecturePerformance Optimization Patterns
Comprehensive patterns for frontend performance: code splitting, lazy loading, prefetching, bundle analysis, memory management, and RUM.
Read article - Frontend ArchitectureRendering Strategies
CSR, SSR, SSG, RSC, and streaming: a comprehensive guide to choosing and mixing rendering strategies for modern frontends.
Read article - Frontend ArchitectureCaching Strategies for the Frontend
HTTP headers, browser cache layers, CDN, application-level caching, service workers, and common pitfalls.
Read article - Frontend ArchitectureWeb Workers and Multithreading
Offload CPU-intensive work from the main thread: Web Workers, Comlink, SharedArrayBuffer, worker pooling, and when to use workers vs requestIdleCallback.
Read article