Back to KB
Difficulty
Intermediate
Read Time
7 min

How I Render 100,000 Rows With Only ~20 DOM Nodes

By Codcompass TeamΒ·Β·7 min read

Architecting High-Performance Data Grids: The Windowing Technique

Current Situation Analysis

Modern web applications routinely manage datasets that exceed 50,000 to 100,000 records. Chat histories, financial ledgers, log aggregators, and inventory systems all share a common architectural trap: developers conflate application state size with rendering surface size. The default mental model assumes that if an array contains 100,000 objects, the DOM must contain 100,000 elements. This assumption breaks browser rendering pipelines.

Every DOM node triggers a cascade of engine work: style resolution, layout calculation, paint compositing, and memory allocation. When you mount a hundred thousand elements synchronously, the main thread blocks for 2–4 seconds during initial paint. Memory consumption spikes to 300–500 MB as the browser maintains hidden layout trees. Once the user attempts to scroll, the event loop becomes saturated with layout thrashing, dropping sustained frame rates below 20 FPS. The interface feels unresponsive, battery drains faster on mobile, and tab crashes become a real risk on constrained devices.

This problem is frequently overlooked because development environments run on high-end hardware with generous memory budgets. Performance degradation only surfaces in production or on mid-tier devices. The industry standard solution is not server-side pagination or infinite scroll, which fragment data continuity. Instead, production-grade applications use windowing (commonly called virtual scrolling). The technique decouples data volume from DOM volume, bounding node creation to the visible viewport rather than the dataset size.

WOW Moment: Key Findings

The performance delta between naive mounting and windowed rendering is not incremental; it is architectural. By restricting DOM creation to the visible window plus a small buffer, you eliminate layout thrashing and garbage collection pressure entirely.

ApproachDOM NodesMemory FootprintInitial Paint (ms)Sustained Scroll FPS
Naive Full Mount100,000~450 MB2,80012–18
Windowed Render~25~12 MB4558–60

This finding matters because it transforms unscalable data grids into responsive, production-ready interfaces. It enables smooth interaction on low-end hardware, reduces client-side memory pressure, and allows developers to keep full dataset state in memory for instant filtering, sorting, and searching without sacrificing UI responsiveness. The browser only pays for what the user actually sees.

Core Solution

Building a windowed renderer from scratch requires shifting from a data-driven DOM model to a viewport-driven DOM model. The implementation relies on four coordinated mechanisms: a scroll container with a synthetic height spacer, index-to-pixel arithmetic, absolute positioning, and a throttled render loop.

1. Data-State Separation

Keep the full dataset in a plain JavaScript array or typed structure. The DOM should never mirror the array length.

interface GridItem {
  id: string;
  label: string;
  metadata: Record<string, unknown>;
}

const dataSource: GridItem[] = Array.from({ length: 100000 }, (_, idx) => ({
  id: `item-${idx}`,
  label: `Record ${idx}`,
  metadata: { status: 'active', priorit

πŸŽ‰ Mid-Year Sale β€” Unlock Full Article

Base plan from just $4.99/mo or $49/yr

Sign in to read the full article and unlock all 635+ tutorials.

Sign In / Register β€” Start Free Trial

7-day free trial Β· Cancel anytime Β· 30-day money-back