Virtual Docs
React Example: Table GitHub
import * as React from 'react'
import { createRoot } from 'react-dom/client'
import { useVirtualizer } from '@tanstack/react-virtual'
import {
flexRender,
getCoreRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table'
import { makeData } from './makeData'
import type { ColumnDef, Row, SortingState } from '@tanstack/react-table'
import type { Person } from './makeData'
import './index.css'
function ReactTableVirtualized() {
const [sorting, setSorting] = React.useState<SortingState>([])
const columns = React.useMemo<Array<ColumnDef<Person>>>(
() => [
{
accessorKey: 'id',
header: 'ID',
size: 60,
},
{
accessorKey: 'firstName',
cell: (info) => info.getValue(),
},
{
accessorFn: (row) => row.lastName,
id: 'lastName',
cell: (info) => info.getValue(),
header: () => <span>Last Name</span>,
},
{
accessorKey: 'age',
header: () => 'Age',
size: 50,
},
{
accessorKey: 'visits',
header: () => <span>Visits</span>,
size: 50,
},
{
accessorKey: 'status',
header: 'Status',
},
{
accessorKey: 'progress',
header: 'Profile Progress',
size: 80,
},
{
accessorKey: 'createdAt',
header: 'Created At',
cell: (info) => info.getValue<Date>().toLocaleString(),
},
],
[],
)
const [data, setData] = React.useState(() => makeData(50_000))
const table = useReactTable({
data,
columns,
state: {
sorting,
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: true,
})
const { rows } = table.getRowModel()
const parentRef = React.useRef<HTMLDivElement>(null)
const virtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 34,
overscan: 20,
})
const virtualRows = virtualizer.getVirtualItems()
// Height of the spacer rows rendered above and below the visible rows
const [paddingTop, paddingBottom] =
virtualRows.length > 0
? [
virtualRows[0].start,
virtualizer.getTotalSize() - virtualRows[virtualRows.length - 1].end,
]
: [0, 0]
const columnCount = table.getVisibleLeafColumns().length
return (
<div ref={parentRef} className="container">
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th
key={header.id}
colSpan={header.colSpan}
style={{ width: header.getSize() }}
>
{header.isPlaceholder ? null : (
<div
{...{
className: header.column.getCanSort()
? 'cursor-pointer select-none'
: '',
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
{{
asc: ' 🔼',
desc: ' 🔽',
}[header.column.getIsSorted() as string] ?? null}
</div>
)}
</th>
)
})}
</tr>
))}
</thead>
<tbody>
{paddingTop > 0 && (
<tr>
<td colSpan={columnCount} style={{ height: paddingTop }} />
</tr>
)}
{virtualRows.map((virtualRow) => {
const row = rows[virtualRow.index]
return (
<tr key={row.id} style={{ height: `${virtualRow.size}px` }}>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
)
})}
</tr>
)
})}
{paddingBottom > 0 && (
<tr>
<td colSpan={columnCount} style={{ height: paddingBottom }} />
</tr>
)}
</tbody>
</table>
</div>
)
}
function App() {
return (
<div>
<p>
Table rows can't be absolutely positioned, so only the visible rows are
rendered, with a spacer row above and below them. The spacers keep the
table as tall as all of its rows, so the sticky header stays in place
while scrolling.
</p>
<ReactTableVirtualized />
<br />
<br />
{process.env.NODE_ENV === 'development' ? (
<p>
<strong>Notice:</strong> You are currently running React in
development mode. Rendering performance will be slightly degraded
until this application is built for production.
</p>
) : null}
</div>
)
}
const container = document.getElementById('root')
const root = createRoot(container!)
const { StrictMode } = React
root.render(
<StrictMode>
<App />
</StrictMode>,
)