List Virtualization

How to render lists of 10,000+ items without crashing the browser by only rendering what's visible on screen.

React5 min readConcept 34 of 46

The Problem with Huge Lists

If you try to render a <ul> with 10,000 <li> elements in React, the browser will likely freeze or crash. The DOM (Document Object Model) is heavy, and forcing the browser to calculate the layout and paint 10,000 nodes at once is incredibly expensive.

List Virtualization (also known as 'windowing') solves this by rendering *only* the items that currently fit inside the user's viewport, plus a small buffer above and below.

The Illusion of Scrolling

Virtualization works by creating a massive, empty 'spacer' div that represents the total height of all 10,000 items (e.g., height: 500,000px). This gives the browser a realistic scrollbar.

Inside that spacer, it absolutely positions only the ~20 items the user can actually see.

As the user scrolls, it doesn't create new DOM nodes. It simply takes the DOM node that scrolled out of view at the top, moves it to the bottom, and updates its data. The total number of DOM nodes stays constant (e.g., 20), no matter how big the list gets.

Using Libraries

You rarely build virtualization from scratch. Instead, you use established libraries like react-window or react-virtuoso.

import { FixedSizeList } from 'react-window';

<FixedSizeList height={400} itemCount={1000} itemSize={50}>

{({ index, style }) => <div style={style}>Row {index}</div>}

</FixedSizeList>

Notice how you pass the style object to your row. This contains the absolute positioning (the top value) calculated by the library based on the current scroll position.

The Viewport Window

Interact with the slider below to 'scroll' through a list of 100 items. Notice how the actual number of DOM nodes stays constant—the nodes just update their data and leapfrog each other as you scroll!

Interactive Diagram: The Leapfrog Effect

4 DOM Nodes rendering 100 Items

Item #0
Browser Viewport
Visible Area
Data Row 0
top: 0pxnode: 0
Data Row 1
top: 40pxnode: 1
Data Row 2
top: 80pxnode: 2
Data Row 3
top: 120pxnode: 3
Elements (DOM Tree)
<!-- Giant Spacer -->
<div style="height: 4000px;">
<div id="node-0"
style="top: 0px;"
>Row 0</div>
<div id="node-1"
style="top: 40px;"
>Row 1</div>
<div id="node-2"
style="top: 80px;"
>Row 2</div>
Buffer
<div id="node-3"
style="top: 120px;"
>Row 3</div>
</div>

The DOM stays tiny. React simply updates the top style and the inner text of these 4 static nodes as you scroll!

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1If you have a virtualized list of 1,000,000 items, and the viewport can fit 10 items, roughly how many DOM nodes are actually rendered by the browser?
  2. 2How does a virtualized list maintain a realistic scrollbar if only 10 items are rendered?
  3. 3When using a library like `react-window`, why must you apply the provided `style` object to your row component?

Remember this

  • Rendering thousands of DOM nodes will crash the browser.
  • List virtualization only renders the items currently visible in the viewport.
  • It works by using a giant spacer div and absolutely positioning a small number of recycled DOM nodes.
  • Use libraries like react-window or react-virtuoso instead of writing it yourself.
  • Beware of accessibility issues: Ctrl+F will not find unrendered items.

Done with this concept?

Mark it complete to track your progress. No login needed.