Using useLegacyTable for Incremental Migration
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, so the bundle is larger than with the tree-shakeable v9 API. Plan to migrate to useTable for better performance and smaller bundles.
When to Use useLegacyTable#
- 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
Basic Usage#
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>
}Type Helpers#
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 |
Using legacyCreateColumnHelper#
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' }),
// ...
]API Differences from v8#
While useLegacyTable aims for v8 compatibility, there are a few differences:
Row Model Functions#
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'Sorting Function Renames#
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.
Caveats and Limitations#
Bundle Size#
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
Deprecation#
useLegacyTable is deprecated and will be removed in a future major version. It exists solely to ease migration. Plan your migration timeline accordingly.
Full-State Subscription Only#
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.
No createTableHook Integration#
useLegacyTable cannot be used with createTableHook. If you want to create reusable table configurations, migrate to the full v9 API.
Migration Path#
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.
Example#
See the Basic useLegacyTable example for a working implementation.