All questions

useVirtualList

Premium

useVirtualList

Rendering 100,000 rows into the DOM will freeze the browser — but the user only ever sees a dozen at a time. Virtualization (windowing) renders only the rows currently in the viewport (plus a small buffer), positioned so the scrollbar still behaves as if all rows existed. useVirtualList does the math: given the item count, row height, and viewport height, it tells you which rows to render and where to place each one.

Implement useVirtualList({ itemCount, itemHeight, containerHeight, overscan = 3 }). Return virtualItems (the visible rows with their absolute offsets), totalHeight (so the scroll area is the right size), startIndex/endIndex, and an onScroll handler that tracks the container's scrollTop.

Signature

function useVirtualList({ itemCount, itemHeight, containerHeight, overscan = 3 }) {
  // returns { virtualItems, totalHeight, startIndex, endIndex, scrollTop, onScroll }
}

Examples

const { virtualItems, totalHeight, onScroll } =
  useVirtualList({ itemCount: rows.length, itemHeight: 32, containerHeight: 400 });

<div style={{ height: 400, overflow: 'auto' }} onScroll={onScroll}>
  <div style={{ height: totalHeight, position: 'relative' }}>
    {virtualItems.map(({ index, offsetTop }) => (
      <Row key={index} style={{ position: 'absolute', top: offsetTop }} {...rows[index]} />
    ))}
  </div>
</div>

Notes

  • Total heightitemCount * itemHeight, so the scroll thumb reflects the full list even though most rows aren't rendered.
  • Visible range — from scrollTop and containerHeight: start = floor(scrollTop / itemHeight), end = floor((scrollTop + containerHeight) / itemHeight).
  • Overscan — render a few extra rows above/below the viewport so fast scrolling doesn't flash blank; clamp start >= 0 and end <= itemCount - 1.
  • Absolute offsets — each visible item's offsetTop = index * itemHeight positions it correctly inside the full-height spacer.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium