mediumReact#48

Virtualization with react-window

Prompt

Use react-window (FixedSizeList) to render 10,000 items efficiently. Each item shows "Row #N". Only visible rows should be in the DOM.

Solution

const items = Array.from({ length: 10000 }, (_, i) => i)

export default function BigList() {
  return (
    <FixedSizeList height={400} itemCount={items.length} itemSize={35} width={300}>
      {({ index, style }) => <div style={style}>Row #{items[index]}</div>}
    </FixedSizeList>
  )
}
Mentor's take

Virtualization exists because the DOM, not React, is the bottleneck for huge lists. Reconciling 10,000 elements is survivable; mounting 10,000 DOM nodes is not — layout, paint, and memory all scale with node count. A virtualized list renders only the rows intersecting the viewport (~12 here) plus a small overscan buffer, and swaps them as you scroll. React.memo cannot save you from this — memo skips re-renders, but the nodes still exist.

The mechanics: with a fixed itemSize, row position is pure arithmetic — offset = index × 35 — so the list knows every row's location without measuring anything (the same reason getItemLayout matters for FlatList in React Native). The children render prop receives index and a style containing position: absolute and top; applying that style is mandatory — drop it and every row stacks at the top, the classic first-time bug. height/width define the viewport; an outer scrollbar stays honest because total content height is also just arithmetic.

The trade-off: FixedSizeList demands uniform row height. Variable content needs VariableSizeList plus a size estimator, which reintroduces measurement cost — so fixed heights are a design win worth fighting for, not just an implementation detail.

Red flag: Answering a "slow long list" question with memoization alone. The senior instinct is: node count is the problem, so windowing is the fix; memo tunes what remains mounted.

Say it: "Virtualization keeps DOM cost proportional to the viewport, not the dataset — fixed row height turns positioning into arithmetic, and the injected style prop is what places each row absolutely."