SuperAPI AI Directory

tanstack-react-table-with-tanstack-virtual

Frontend & websites

View repository

Updated 10-16 00:02

About this skill

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

import { useRef } from 'react'
import { useVirtualizer } from '@tanstack/react-virtual'

function VirtualBody({ table }: { table: any }) { const scrollRef = useRef(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} /> ))} ))} ) }


## 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

<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:

const features = tableFeatures({ rowVirtualizer: useVirtualizer(options) })

Correct:

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:

const virtualizer = useVirtualizer({
  count: data.length,
  getScrollElement,
  estimateSize,
})

Correct:

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:

<tr ref={virtualizer.measureElement}>{row.id}</tr>

Correct:

<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:

<tbody>{virtualizer.getVirtualItems().map(renderRow)}</tbody>

Correct:

<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.