| name | preact/migrate-v8-to-v9 |
| description | Mechanical breaking-change migration from TanStack Table v8 to v9 for `@tanstack/preact-table`. Maps every old-shaped option, helper, type, and method an agent will reproduce from v8 muscle memory to its v9 equivalent: `useReactTable` → `useTable`, per-row-model `get*RowModel` options → `_features` + `_rowModels`, plain column helpers → typed column helpers, `state` + `on*Change` → `atoms`, `flexRender` → `table.FlexRender`, and core type renames. Routing keywords: v8 to v9, migration, useReactTable, table v8 preact, get*RowModel, _features.
|
| type | lifecycle |
| library | tanstack-table |
| framework | preact |
| library_version | 9.0.0-alpha.48 |
| requires | ["setup","state-management","column-definitions"] |
| sources | ["TanStack/table:docs/framework/preact/preact-table.md","TanStack/table:docs/framework/preact/guide/table-state.md","TanStack/table:docs/framework/react/guide/use-legacy-table.md","TanStack/table:packages/preact-table/src/useTable.ts"] |
The Preact adapter mirrors the React v9 surface, so any v8 → v9 migration guide for React applies almost line-for-line. There is no useLegacyTable shim in @tanstack/preact-table — migrate directly.
The Core Mapping
| v8 (Preact / React-compatible) | v9 (@tanstack/preact-table) |
|---|
useReactTable(opts) | useTable(opts, selector?) |
getCoreRowModel: getCoreRowModel() | included by default — _rowModels: {} is valid |
getSortedRowModel: getSortedRowModel() | _features: { rowSortingFeature } + _rowModels: { sortedRowModel: createSortedRowModel(sortFns) } |
getFilteredRowModel: getFilteredRowModel() | _features: { columnFilteringFeature, globalFilteringFeature } + _rowModels: { filteredRowModel: createFilteredRowModel(filterFns) } |
getPaginationRowModel: getPaginationRowModel() | _features: { rowPaginationFeature } + _rowModels: { paginatedRowModel: createPaginatedRowModel() } |
getGroupedRowModel: getGroupedRowModel() | _features: { columnGroupingFeature } + _rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns) } |
getExpandedRowModel: getExpandedRowModel() | _features: { rowExpandingFeature } + _rowModels: { expandedRowModel: createExpandedRowModel() } |
getFacetedRowModel, getFacetedUniqueValues, getFacetedMinMaxValues | _features: { columnFacetingFeature, globalFacetingFeature } + _rowModels: { facetedRowModel: createFacetedRowModel(facetedFns), facetedUniqueValues: createFacetedUniqueValues(), facetedMinMaxValues: createFacetedMinMaxValues() } |
flexRender(def, ctx) | <table.FlexRender cell={cell} /> / header={...} / footer={...} |
state, on*Change (only) | still supported, plus atoms (preferred per slice) |
createColumnHelper<TData>() | createColumnHelper<typeof _features, TData>() — both generics required |
ColumnDef<TData, TValue> | ColumnDef<TFeatures, TData, TValue> — TFeatures is now the first generic |
Table<TData> | Table<TFeatures, TData> |
Source: docs/framework/preact/preact-table.md; docs/framework/preact/guide/table-state.md.
Migration Steps
1. Update the package import
import {
useReactTable,
getCoreRowModel,
getSortedRowModel,
flexRender,
type ColumnDef,
} from '@tanstack/preact-table'
import {
useTable,
tableFeatures,
rowSortingFeature,
createSortedRowModel,
sortFns,
type ColumnDef,
} from '@tanstack/preact-table'
2. Declare _features and _rowModels
Replace each get*RowModel: get*RowModel() option with a feature import + a row-model factory.
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
})
const _features = tableFeatures({
rowSortingFeature,
columnFilteringFeature,
rowPaginationFeature,
})
const table = useTable({
_features,
_rowModels: {
sortedRowModel: createSortedRowModel(sortFns),
filteredRowModel: createFilteredRowModel(filterFns),
paginatedRowModel: createPaginatedRowModel(),
},
columns,
data,
})
Move _features to module scope. Reference stability matters — see tanstack-table/preact/production-readiness.
3. Update column types and helpers
TFeatures is now the first generic on ColumnDef, Table, and createColumnHelper.
const columnHelper = createColumnHelper<Person>()
const columns: ColumnDef<Person>[] = columnHelper.columns([
])
const columnHelper = createColumnHelper<typeof _features, Person>()
const columns: Array<ColumnDef<typeof _features, Person>> =
columnHelper.columns([
])
4. Replace flexRender calls with table.FlexRender
<th>{flexRender(header.column.columnDef.header, header.getContext())}</th>
<td>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
<th>{header.isPlaceholder ? null : <table.FlexRender header={header} />}</th>
<td><table.FlexRender cell={cell} /></td>
flexRender is still exported for advanced cases, but table.FlexRender (or the standalone FlexRender component) handles grouping placeholder/aggregated branches for you.
Source: packages/preact-table/src/FlexRender.tsx.
5. Move external state to atoms (recommended)
state + on*Change still works, but v9 prefers slice atoms for fine-grained reactivity.
const [sorting, setSorting] = useState<SortingState>([])
const table = useTable({
_features,
_rowModels,
columns,
data,
state: { sorting },
onSortingChange: setSorting,
})
import { useCreateAtom } from '@tanstack/preact-store'
const sortingAtom = useCreateAtom<SortingState>([])
const table = useTable({
_features,
_rowModels,
columns,
data,
atoms: { sorting: sortingAtom },
})
Source: examples/preact/basic-external-atoms/src/main.tsx.
6. Drop onStateChange
The v8-style global onStateChange is gone. Subscribe per-slice with on*Change, an external atom, or table.store.subscribe(...) if you really need every change.
Source: docs/framework/preact/guide/table-state.md.
Common Mistakes
CRITICAL Keeping get*RowModel options after upgrading
Wrong:
const table = useTable({
_features,
_rowModels: {},
columns,
data,
getSortedRowModel: getSortedRowModel(),
})
table.setSorting([{ id: 'age', desc: true }])
Correct:
const _features = tableFeatures({ rowSortingFeature })
const table = useTable({
_features,
_rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
columns,
data,
})
v9 doesn't read the get*RowModel options. The row model only runs for stages registered in _rowModels, and the feature only mounts if it is in _features.
Source: docs/framework/preact/preact-table.md.
CRITICAL Forgetting to register a feature whose API you are calling
Wrong:
const _features = tableFeatures({})
const table = useTable({ _features, _rowModels: {}, columns, data })
table.getIsAllRowsSelected()
row.toggleSelected(true)
Correct:
const _features = tableFeatures({ rowSelectionFeature })
const table = useTable({
_features,
_rowModels: {},
columns,
data,
enableRowSelection: true,
})
table.getIsAllRowsSelected()
v9 generates feature APIs and state slices only for registered features. This is the #1 v9 trap.
Source: docs/guide/features.md.
HIGH Re-using getCoreRowModel: getCoreRowModel() from v8
Wrong:
const table = useTable({
_features,
_rowModels: {},
columns,
data,
getCoreRowModel: getCoreRowModel(),
})
Correct:
const table = useTable({ _features, _rowModels: {}, columns, data })
The core row model is always included in v9. There is no getCoreRowModel option.
Source: docs/framework/preact/preact-table.md.
HIGH Single-generic column helper / ColumnDef
Wrong:
const columnHelper = createColumnHelper<Person>()
const columns: ColumnDef<Person>[] = [
]
Correct:
const columnHelper = createColumnHelper<typeof _features, Person>()
const columns: Array<ColumnDef<typeof _features, Person>> = [
]
TFeatures is the first generic for nearly every public type in v9. Without it, types degrade to any for feature methods.
Source: docs/framework/preact/preact-table.md.
HIGH Reimplementing built-ins (the #1 AI tell)
Wrong:
Correct: register the matching feature in _features, register its factory in _rowModels, and use the feature API. v9 ships built-ins for sorting, filtering, pagination, grouping, expanding, faceting, row selection, column visibility/order/pinning/sizing, and row pinning.
Source: docs/guide/features.md.
MEDIUM Calling flexRender directly when grouping is on
Wrong:
<td>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
Correct:
<td>
<table.FlexRender cell={cell} />
</td>
<table.FlexRender cell={...}> handles aggregated / placeholder cells when columnGroupingFeature is registered. Raw flexRender does not.
Source: packages/preact-table/src/FlexRender.tsx.
See Also
tanstack-table/preact/getting-started — green-field v9 setup.
tanstack-table/preact/table-state — atom model and Subscribe patterns.
tanstack-table/preact/production-readiness — perf, tree-shaking, stable refs.