Blog>
Snippets

Calculating Visible Rows for Virtualization

Demonstrate how to calculate which rows are visible in the viewport, using the scroll position and row heights, to implement row virtualization in React TanStack Table.
import React, { useRef, useState, useEffect } from 'react';
import { useVirtual } from '@tanstack/react-virtual';
Import necessary hooks from React and TanStack's react-virtual for row virtualization.
const parentRef = useRef();
Create a ref for the parent container to measure its dimensions.
const [rows, setRows] = useState(Array.from({length: 10000}, (_, index) => ({
  id: index,
  height: 35 + Math.floor(Math.random() * 3) * 15, // Random row heights for demonstration
})));
Initialize state with an array of rows, each having a unique ID and potentially different heights to simulate a real dataset.
const rowVirtualizer = useVirtual({
  size: rows.length, // Total number of rows
  parentRef, // The parent element ref
  estimateSize: React.useCallback(() => 50, []), // Estimated row height
  overscan: 5, // Number of rows to render outside of the visible area
});
Initialize the row virtualizer from @tanstack/react-virtual, using the parent ref, total number of rows, estimated row height, and overscan.
useEffect(() => {
  const handleResize = () => {
    // Recalculate virtual rows on window resize
    rowVirtualizer.recomputeSizes();
  };
  window.addEventListener('resize', handleResize); 
  return () => window.removeEventListener('resize', handleResize);
}, []);
Recalculate virtual rows when the window is resized to ensure the correct number of rows are rendered.
const renderRow = virtualRow => {
  const row = rows[virtualRow.index];
  return (
    <div
      key={row.id}
      style={{
        position: 'absolute',
        top: 0,
        left: 0,
        width: '100%',
        height: `${row.height}px`,
        transform: `translateY(${virtualRow.start}px)`
      }}
    >
      Row {row.id}
    </div>
  );
};
A function to render a row. Positions the row based on its virtual position and applies a unique transform for correct vertical placement.
return (
  <div ref={parentRef} style={{ overflow: 'auto', maxHeight: '80vh' }}>
    <div
      style={{
        height: `${rowVirtualizer.totalSize}px`,
        position: 'relative',
        width: '100%',
      }}
    >
      {rowVirtualizer.virtualItems.map(renderRow)}
    </div>
  </div>
);
The component return JSX. It renders a scrolling container for the rows and a div that represents the total size of the rows, including those not rendered. The virtual rows are mapped to the renderRow function.