Row Virtualization

Mount only the visible row window inside a scrollable viewport so very large datasets scroll smoothly. Mounted rows are real table rows sharing the header <colgroup>, keeping header/body columns pixel-aligned and every interactive element inside cells clickable at any scroll depth.

const table = useTable({
  data,
  columns,
  enableVirtualization: true,
  rowHeight: 40, // or (index) => number for variable heights
  virtualViewportHeight: 480,
})

Notes

  • Virtualization bypasses client pagination automatically — the whole (sorted/grouped) dataset scrolls, so the old pageSize: 100_000 workaround is no longer needed. manualPagination (server-side) is still respected, and you should not combine virtualization with the <Pagination> control.
  • virtualViewportHeight sets the scroll viewport in px; without it a heuristic (~20 rows, capped at 800px) applies, which can overflow shorter styled containers. Measure fixed-height panels and pass the available height explicitly.
  • overscan (default 5) mounts extra rows around the window; exact per-row heights can come from Pretext via pretextHeights / pretextPrefixSums.
  • Pinned columns stay frozen to the viewport edges during horizontal scroll: the header and body share one scroll container, so pinned header and body cells move together. The horizontal scrollbar sits at the bottom of the virtualViewportHeight viewport, reachable without scrolling to the last row.
  • The Pretext hooks (useTableRowHeights, usePretextMeasurement) are imported from the @zvndev/yable-react/pretext subpath and need the optional @chenglou/pretext peer installed. The subpath keeps @chenglou/pretext out of the main entry, so importing @zvndev/yable-react never forces your bundler (e.g. Next.js/Turbopack) to resolve it: import { useTableRowHeights } from '@zvndev/yable-react/pretext'.