AniUI Academy

Virtualization for Large Lists

Why rendering ten thousand DOM nodes for a ten-thousand-row list is the wrong default, and how windowing renders only what's actually visible.

10 min read

A list with a few dozen items can render every one of them without a second thought. A list with ten thousand rows — a chat history, a spreadsheet, a social feed — cannot, and rendering all of them anyway is one of the most common, most fixable performance mistakes in frontend engineering.

Why "just render them all" breaks down

Every DOM node the browser creates costs real memory and participates in every layout calculation the browser does from then on, whether or not it's currently visible. A list of 10,000 rows when only 20 fit in the viewport means 9,980 DOM nodes exist purely to sit off-screen, never seen, still costing memory and still slowing down layout, scrolling, and any re-render that touches the list (React has to diff all 10,000, even if the visible 20 are the only ones that actually changed).

Virtualization: render only what's visible

Virtualization (also called windowing) renders a small subset of the actual list — what's currently visible, plus a small buffer above and below for smooth scrolling — and simulates the rest.

  1. Step 1

    Compute total scrollable height

    From item count and (known or estimated) item height, so the scrollbar behaves as if all 10,000 items really existed.

  2. Step 2

    Determine the visible window

    Based on current scroll position and viewport height, calculate which item indices are actually on screen.

  3. Step 3

    Render only that window (plus a buffer)

    Real DOM nodes exist only for these items — typically dozens, not thousands.

  4. Step 4

    User scrolls

    The window recalculates; DOM nodes are recycled and re-populated with new data rather than the list creating thousands of new nodes.

Scroll position determines which items get real DOM nodes — everything else is represented only by its contribution to total scrollable height.

The result, from the user's perspective, is indistinguishable from a real 10,000-row list — the scrollbar is the right size, scrolling feels continuous, every row is reachable. From the browser's perspective, it's only ever dealing with the handful of DOM nodes actually needed to represent what's on screen right now.

Fixed vs. variable item heights

Fixed-height virtualization is the simple case: if every row is exactly 50px tall, item N's position is just N * 50, so scroll position maps directly to visible indices with pure arithmetic — no measurement needed at all.

Variable-height virtualization (chat messages of different lengths, cards with different content) is genuinely harder: the list has to measure each item as it renders — or start from an estimated height and correct itself once the real measurement comes in — and maintain a position map that shifts as those real measurements arrive. This is exactly why variable-height virtualization can visibly "jump" slightly during fast scrolling if the estimate was off, in a way fixed-height virtualization never does.

When it's worth the complexity

Virtualization is not a default to reach for on every list — it's real implementation complexity (correct keyboard navigation, correct focus management, correct behavior with browser find-in-page, which doesn't see off-screen "virtual" content by default) that only pays for itself once a list is large enough that rendering all of it genuinely costs something noticeable. A list of 50 items doesn't need it. A list of many thousands almost certainly does. The concrete signal worth naming in an interview isn't a specific number, it's this test: does the list's length scale with something the user controls or generates (search results, a chat history, an infinite feed) rather than a small, fixed set — that's the shape of list that eventually needs windowing.

What to remember

  • Rendering every row of a large list, most of it off-screen, costs real memory and slows down layout, scrolling and re-renders — regardless of whether it's currently visible.
  • Virtualization renders only the visible window (plus a small buffer), simulating the rest via a computed total scrollable height.
  • Fixed item heights make virtualization pure arithmetic; variable heights require measurement and a position map that can shift as real sizes arrive.
  • Reach for virtualization once a list's length scales with user-generated or unbounded data, not for small, fixed-size lists where the added complexity outweighs the benefit.

Check yourself

3 questions · pass 3/3 to unlock Memoization Strategy and Its Costs

up to 50
  1. 1.A table renders all 10,000 rows of a dataset into the DOM at once, even though only about 20 are visible in the viewport at a time. What's the direct cost of this approach?

  2. 2.What does a virtualized (windowed) list actually render into the DOM at any given moment?

  3. 3.Why is a fixed item height a much simpler case for virtualization than variable, dynamically-measured item heights?

3 left to answer