@tanstack/react-table
Version:
Headless UI for building powerful tables & datagrids for React.
198 lines (155 loc) • 5 kB
Markdown
name: with-tanstack-virtual
description: >
Virtualize final React Table row or column models with TanStack Virtual. Load for useVirtualizer counts, scroll elements, stable keys, data-index measurement, dynamic heights, sticky headers/columns, grid/flex geometry, or infinite fetching; Virtual is renderer composition, not a Table feature.
metadata:
type: composition
library: '@tanstack/react-table'
library_version: '9.0.0'
framework: react
requires:
- '@tanstack/table-core#core'
- getting-started
- table-state
sources:
- 'TanStack/table:docs/framework/react/guide/virtualization.md'
- 'TanStack/table:examples/react/virtualized-rows'
- 'TanStack/table:examples/react/virtualized-columns'
- 'TanStack/table:examples/react/virtualized-infinite-scrolling'
This skill builds on `@tanstack/table-core#core`, `getting-started`, and `table-state`. Build the Table model first, then virtualize its final rendered rows or visible columns.
## Setup
```tsx
import { useRef } from 'react'
import { useVirtualizer } from '@tanstack/react-virtual'
function VirtualBody({ table }: { table: any }) {
const scrollRef = useRef<HTMLDivElement>(null)
const rows = table.getRowModel().rows
const virtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => scrollRef.current,
estimateSize: () => 36,
getItemKey: (index) => rows[index].id,
overscan: 5,
})
return (
<div ref={scrollRef} style={{ height: 400, overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map((item) => (
<div
key={rows[item.index].id}
data-index={item.index}
ref={virtualizer.measureElement}
style={{
position: 'absolute',
transform: `translateY(${item.start}px)`,
width: '100%',
}}
>
{rows[item.index].getAllCells().map((cell: any) => (
<span
key={cell.id}
style={{
display: 'inline-block',
width: cell.column.getSize(),
}}
>
<table.FlexRender cell={cell} />
</span>
))}
</div>
))}
</div>
</div>
)
}
```
## Core Patterns
### Keep the virtualizer near its render loop
```tsx
const rows = table.getRowModel().rows
const rowVirtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => container.current,
estimateSize: () => 36,
})
```
This limits unrelated parent updates and keeps count, measurement, and rendered items together.
### Use Table sizes in renderer CSS
```tsx
<td style={{ width: cell.column.getSize() }}>
<table.FlexRender cell={cell} />
</td>
```
Table calculates size state; the renderer must apply it.
## Common Mistakes
### HIGH Registering Virtual as a feature
Wrong:
```tsx
const features = tableFeatures({ rowVirtualizer: useVirtualizer(options) })
```
Correct:
```tsx
const rows = table.getRowModel().rows
const virtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => scrollRef.current,
estimateSize: () => 36,
})
```
Virtual controls rendering geometry and is not a Table feature or row model.
Source: `docs/framework/react/guide/virtualization.md`
### HIGH Virtualizing raw input data
Wrong:
```tsx
const virtualizer = useVirtualizer({
count: data.length,
getScrollElement,
estimateSize,
})
```
Correct:
```tsx
const rows = table.getRowModel().rows
const virtualizer = useVirtualizer({
count: rows.length,
getScrollElement,
estimateSize,
})
```
Raw data ignores filtering, sorting, expansion, grouping, and pagination already applied by Table.
Source: `examples/react/virtualized-rows`
### HIGH Measuring against incomplete identity
Wrong:
```tsx
<tr ref={virtualizer.measureElement}>{row.id}</tr>
```
Correct:
```tsx
<tr key={row.id} data-index={item.index} ref={virtualizer.measureElement}>
{row.id}
</tr>
```
Dynamic measurement needs the virtual index, and stable row identity prevents measurements moving to the wrong row.
Source: `examples/react/virtualized-rows`
### HIGH Omitting positioning geometry
Wrong:
```tsx
<tbody>{virtualizer.getVirtualItems().map(renderRow)}</tbody>
```
Correct:
```tsx
<tbody
style={{
display: 'grid',
height: virtualizer.getTotalSize(),
position: 'relative',
}}
>
{virtualizer.getVirtualItems().map(renderRow)}
</tbody>
```
Virtual only computes positions; the renderer must provide total spacer size and position items using each virtual start.
Source: `examples/react/virtualized-rows`
## API Discovery
Inspect `node_modules/@tanstack/react-table/dist/index.d.ts` for Table render APIs and installed `node_modules/@tanstack/react-virtual/dist/` for the exact virtualizer options. Copy layout contracts from the maintained example matching rows, columns, or infinite loading.