The useLegacyTable hook provides a compatibility layer that accepts the v8-style API while using v9 under the hood. This is useful for teams that need to migrate incrementally or have large codebases where a full migration isn't immediately practical.
Warning: useLegacyTable is deprecated and intended only as a temporary migration aid. It includes all features by default, resulting in a larger bundle size compared to the tree-shakeable v9 API. Plan to migrate to useTable for better performance and smaller bundles.
You have an existing v8 codebase and need to upgrade dependencies
You want to migrate incrementally, one table at a time
You need v9 compatibility but don't have time for a full migration yet
import { useState } from 'react'
import { flexRender } from '@tanstack/react-table'
import {
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
legacyCreateColumnHelper,
useLegacyTable,
} from '@tanstack/react-table/legacy'
import type {
ColumnFiltersState,
PaginationState,
SortingState,
} from '@tanstack/react-table'
import type {
LegacyColumn,
LegacyColumnDef,
LegacyRow,
} from '@tanstack/react-table/legacy'
interface Person {
name: string
email: string
age: number
}
const columnHelper = legacyCreateColumnHelper<Person>()
const columns: LegacyColumnDef<Person>[] = [
columnHelper.accessor('name', { header: 'Name' }),
columnHelper.accessor('email', { header: 'Email' }),
columnHelper.accessor('age', { header: 'Age' }),
columnHelper.display({ id: 'actions', header: 'Actions' }),
]
function MyTable({ data }: { data: Person[] }) {
const [sorting, setSorting] = useState<SortingState>([])
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
})
// useLegacyTable accepts the v8-style API
const table = useLegacyTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
state: { sorting, columnFilters, pagination },
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onPaginationChange: setPagination,
})
return (
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{header.column.getCanFilter() ? (
<Filter column={header.column} />
) : null}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getAllCells().map((cell) => (
<td key={cell.id}>
{cell.column.id === 'actions' ? (
<RowActions row={row} />
) : (
flexRender(cell.column.columnDef.cell, cell.getContext())
)}
</td>
))}
</tr>
))}
</tbody>
</table>
)
}
function Filter({ column }: { column: LegacyColumn<Person> }) {
return (
<input
value={(column.getFilterValue() as string) ?? ''}
onChange={(e) => column.setFilterValue(e.target.value)}
placeholder="Filter..."
/>
)
}
function RowActions({ row }: { row: LegacyRow<Person> }) {
return <button onClick={() => console.log(row.original)}>Edit</button>
}When using useLegacyTable, use these type helpers for proper TypeScript support:
| Type | Description |
|---|---|
| LegacyColumnDef<TData> | Column definition type (equivalent to v8's ColumnDef<TData>) |
| LegacyColumn<TData> | Column instance type |
| LegacyRow<TData> | Row instance type |
| LegacyCell<TData> | Cell instance type |
| LegacyTable<TData> | Table instance type |
| legacyCreateColumnHelper<TData>() | Column helper with StockFeatures pre-bound; only requires TData |
Use legacyCreateColumnHelper instead of createColumnHelper. It has StockFeatures pre-bound, so you only need to specify TData:
import { legacyCreateColumnHelper } from '@tanstack/react-table/legacy'
import type { LegacyColumnDef } from '@tanstack/react-table/legacy'
const columnHelper = legacyCreateColumnHelper<Person>()
const columns: LegacyColumnDef<Person>[] = [
columnHelper.accessor('name', { header: 'Name' }),
// ...
]While useLegacyTable aims for v8 compatibility, there are a few differences:
The get*RowModel() functions are imported from @tanstack/react-table/legacy:
import {
getCoreRowModel,
getFilteredRowModel,
getSortedRowModel,
getPaginationRowModel,
getExpandedRowModel,
getGroupedRowModel,
getFacetedRowModel,
getFacetedMinMaxValues,
getFacetedUniqueValues,
} from '@tanstack/react-table/legacy'Note that in v9, sorting-related APIs have been renamed. If you're using custom sorting functions in column definitions:
| v8 | v9 |
|---|---|
| sortingFn | sortFn |
The legacy table adapter handles this internally for built-in sorting, but if you're defining custom sorting functions, be aware of the rename.
useLegacyTable includes all features by default, similar to v8. This means:
No tree-shaking benefits
Bundle size is much larger than v8, since each feature has grown since v8 and you pay for all of them
The tree-shakeable v9 API exists so TanStack Table can add features over time without bloating everyone's bundles; only users who opt into a feature pay for it
If bundle size is a concern, prioritize migrating to the full v9 API
useLegacyTable is deprecated and will be removed in a future major version. It exists solely to ease migration. Plan your migration timeline accordingly.
useLegacyTable always subscribes to the entire table state, so the component that creates the table re-renders on every state change, just like v8. The fine-grained rendering optimizations of v9 (narrow useTable selectors) are not available through the legacy hook.
useLegacyTable cannot be used with createTableHook. If you want to create reusable table configurations, migrate to the full v9 API.
Once you're ready to migrate to the full v9 API:
Replace useLegacyTable with useTable
Define your features using tableFeatures()
Convert get*RowModel() options to rowModels
Update types from Legacy* to the standard v9 types
See the main migration guide for complete instructions.
See the Basic useLegacyTable example for a working implementation.