Virtual Scrolling

Virtual scrolling renders only the items currently needed for the viewport instead of mounting an entire large collection. This reduces DOM size and can lower rendering, layout, and update costs for large lists.

A virtualized list maintains a small set of DOM elements corresponding to the visible region plus a buffer. As the user scrolls, the implementation reuses or updates those elements so different records appear without creating thousands of simultaneous nodes.

The main challenge is preserving the illusion of a complete list. The scroll container needs a total scrollable height, while only a small portion is actually represented by rendered elements. Fixed-height rows make this straightforward because an item's position can be calculated from its index; variable-height rows require measurement and bookkeeping.

Virtual scrolling is especially useful when a collection contains hundreds or thousands of items and each item has meaningful rendering cost. It does not automatically make every interface faster: for small lists, virtualization can add complexity, measurement work, and scrolling coordination without providing a meaningful benefit.

A robust implementation also needs to handle keyboard focus, accessibility, dynamic content, resizing, and data changes. In component frameworks, established virtualization libraries are often preferable to implementing these mechanics manually because edge cases around measurement and scrolling are easy to miss.

Key points

Interview questions

What problem does virtual scrolling solve?

Virtual scrolling limits how many list items are mounted at one time. Instead of putting every record into the DOM, it renders the visible range and usually a small buffer around it. This reduces DOM size and the amount of rendering and layout work associated with large collections. The user still experiences a continuous scrollable list.

How would you implement virtualization for fixed-height rows?

Given a constant row height, I would derive the first visible index from `scrollTop / rowHeight`. I would calculate how many rows fit in the viewport, add an overscan buffer, and render only that index range. The container would retain a total height of `itemCount * rowHeight`, while the rendered rows would be positioned according to their logical indices. This makes scrolling calculations predictable and avoids mounting the entire collection.

What makes variable-height virtualization harder?

With variable heights, an item's position cannot be calculated from its index using one constant multiplier. The implementation needs measured heights or an estimate and must maintain enough information to map scroll offsets to item indices. Measurements can change after rendering, so the virtualizer may need to adjust positions and sometimes the scroll offset. This is why variable-height lists generally require more bookkeeping than fixed-height lists.

What is overscan, and what trade-off does it introduce?

Overscan means rendering some items just outside the visible viewport. It helps prevent blank regions when scrolling quickly because nearby content is already mounted before it enters view. More overscan improves that safety margin but increases the number of mounted elements and therefore some rendering cost. The appropriate amount depends on item complexity, scroll behavior, and device performance.

What production issues would you investigate in a virtualized list?

I would check scroll performance, item measurement stability, focus behavior, accessibility semantics, and behavior during resizing or data updates. I would also verify that keys remain stable so reused elements do not accidentally retain state from another record. For variable-height content, I would look for scroll jumps caused by late measurements. Finally, I would profile before and after virtualization because reducing DOM nodes is useful only if the actual bottleneck is rendering or related work.