Technology handbook
TanStack Virtual
Virtualized lists, grids, dynamic measurement, scrolling and accessibility at scale.
- Topics
- 24
- Sections
- 4
Hooks
useWindowVirtualizer
Virtualize against the window's own scrolling instead of a scroll container.
- The page itself scrolls, so the list has no inner scrollbar of its own.
- Offsets are measured from the document, which the hook accounts for.
- This is the shape a long article feed or a search results page wants.
Virtualizer options
count, estimateSize, getScrollElement and overscan are the whole contract.
- The count is the logical length, not the number of rendered nodes.
- The size estimate only has to be close; measurement corrects it.
- Overscan trades a few extra renders for a quieter fast scroll.
Virtualizer instance methods
getVirtualItems, getTotalSize and measure are what the hook hands back to render with.
- The virtual items carry an index, an offset and a size to render with.
- Total size is what the spacer element is sized to.
- Calling measure after a data change recalculates the cached sizes.
scrollToIndex and scrollToOffset
Move to a row programmatically, with an alignment that does not surprise the reader.
- Scrolling by index works even when the target is not currently rendered.
- Alignment decides whether the row lands at the top, centre or nearest edge.
- An unmeasured target may need a second pass once its real height is known.
Virtualizer in a dialog
Re-measure when a scroll container appears or resizes, or the first paint is the wrong height.
- A container that starts hidden has no size, so the first measurement is zero.
- Re-measure when the dialog opens or the list renders one screen of items.
- A resize observer keeps it correct when the dialog is resized afterwards.
Virtualizer with a ref
Give the hook the scroll element through a ref, and keep its size stable.
- The hook reads the scroll element through a callback, not at first render.
- A ref that changes identity every render restarts the measurement.
- The element needs a fixed height and its own overflow for scrolling to exist.
Virtualization fundamentals
useVirtualizer
Compute visible items from a count, a scroll element and an item size estimate.
- Only the visible window is rendered, whatever the list length.
- The hook returns positions; the markup still decides how to draw them.
- Ten thousand rows cost about as much to render as twenty.
Scroll element
Point the virtualizer at the element that actually scrolls.
- The element must actually scroll, with a height and an overflow rule.
- Pointing at a parent that does not scroll produces a list stuck at the top.
- Nested scroll containers need the innermost one, not the page.
estimateSize
Provide a starting height so offsets are usable before measurement.
- A rough estimate is enough to place items before anything is measured.
- A wildly wrong estimate makes the scrollbar jump as real sizes arrive.
- For uniform rows the estimate is exact and no measurement is needed.
Total size
Size the inner container to the full list so the scrollbar is honest.
- The spacer is what gives the scrollbar its correct length.
- Without it the container is only as tall as the rendered window.
- Total size changes as items are measured, so it is read on every render.
Item positioning
Place each visible item at its computed offset rather than in flow.
- Each item is absolutely placed at its own offset inside the spacer.
- Normal flow cannot work, because the items above it are not rendered.
- A transform on the container is the cheaper alternative to per-item offsets.
Overscan
Render a few extra items beyond the viewport to hide fast scrolling.
- Extra rows above and below cover the gap during a fast flick.
- Too much overscan renders work that is never seen.
- Tune it against a real device, since the right number depends on row cost.
Dynamic content
measureElement
Report an item's real size after render so later offsets correct themselves.
- Real heights replace the estimate as each item renders.
- Offsets after a re-measured item shift, which is why order matters.
- The callback must be attached to the element that owns the height.
Variable heights
Handle rows whose height depends on content without layout jumps.
- A list of mixed content cannot be described by one fixed size.
- Measuring on render keeps the scrollbar honest as the reader moves.
- An image without reserved space resizes the row after it has been measured.
Horizontal virtualization
Virtualize columns the same way rows are virtualized.
- The same model applies along the horizontal axis.
- Widths are estimated and measured exactly as heights are.
- A wide timeline or a column strip is the usual case.
Grid virtualization
Combine row and column virtualizers for a two-dimensional surface.
- Two virtualizers together give a window in both directions.
- Each cell is positioned from a row offset and a column offset.
- A spreadsheet surface only renders the cells actually on screen.
Sticky items
Keep a header or group label fixed while its section scrolls.
- A section header is rendered outside the virtual window while it is pinned.
- Its index has to be tracked, since the item under it keeps changing.
- Sticky positioning needs a container the header can stick within.
Window scrolling
Virtualize against the page itself rather than a bounded element.
- Virtualizing the page avoids an inner scrollbar that traps the wheel.
- The list start offset from the document top has to be accounted for.
- It behaves better on mobile, where nested scroll areas are awkward.
Interaction & integration
scrollToIndex
Move to a specific item with a chosen alignment.
- Jumping to an item is what a deep link into a long list needs.
- Alignment decides how much context is visible around the target.
- Combine it with focus so a keyboard reader lands on the row, not the container.
Infinite loading
Fetch the next page as the reader approaches the end of the loaded range.
- Trigger the next page from the rendered range, not from a scroll handler.
- Fetch before the reader reaches the end so the list never stops.
- Appending items changes the count, which resizes the spacer.
Keyboard navigation
Keep focus correct when the focused item scrolls out of the rendered range.
- Moving focus to a row that is not rendered loses focus entirely.
- Scroll the target into the rendered range first, then move focus.
- Focus lost to an unmounted row silently returns to the document body.
Accessibility
Expose position and size so assistive technology reports the real list length.
- The rendered subset must still report the full list size and each row's position.
- Without that, a screen reader announces twenty items out of ten thousand.
- Use a real list or grid role rather than unlabelled positioned divs.
Combining with Table
Virtualize a table's row model without losing sorting or selection.
- The row model stays complete; only its rendering is windowed.
- Sorting and selection operate on all rows, not on the visible ones.
- Measure the row element, since a cell can grow the row unexpectedly.
Performance measurement
Profile scroll performance instead of raising overscan by guesswork.
- Profile a scroll to see whether the cost is layout, paint or the row component.
- Raising overscan hides a slow row rather than fixing it.
- A heavy cell renderer is usually the real cause of a stuttering list.
Sources
- TanStack Virtual official documentation