Virtualization (also called windowing) renders only the rows visible in the viewport, plus a small extra buffer. This buffer is the overscan: a few rows just above and below the screen, so fast scrolling does not show blank space.
Spacers or absolute positioning keep the scrollbar the size of the full list. As the user scrolls, rows are reused. A list of 10,000 items then costs about 30 DOM nodes instead of 10,000.
You need it when rendering the whole list or grid causes problems:
- The first render is slow.
- It uses too much memory.
- Scrolling is janky (it stutters).
This usually means hundreds to thousands of complex rows. Examples are feeds, chat history, logs and large tables.
It has trade-offs:
- Rows with different heights need measuring.
- The browser's find-in-page cannot see rows that are not rendered.
- Restoring the scroll position needs care.
- Screen readers only see rendered items. So set
aria-setsizeandaria-posinsetwhere appropriate. They tell the screen reader the full list size and each item's position.
For moderate lists, CSS content-visibility: auto is a cheaper alternative. Libraries include TanStack Virtual and react-window.
- Render only the visible rows plus a small overscan buffer.
- You need it for long or complex lists.
- Rows with variable heights need measuring.
- Find-in-page cannot see hidden rows, and a11y (accessibility) needs extra ARIA attributes.