Read @tanstack/table-core#migrate-v8-to-v9, getting-started, and table-state. Use this as the exhaustive Solid migration checklist. Check the installed declarations before emitting APIs for a different v9 version.
Framework prerequisite: Solid 1.3 or newer (solid-js >=1.3).
Target architecture
import {
createSortedRowModel,
createTable,
rowSortingFeature,
tableFeatures,
} from '@tanstack/solid-table'
const features = tableFeatures({
rowSortingFeature,
sortedRowModel: createSortedRowModel(),
})
const table = createTable({ features, columns, get data() { return data() }, })
Keep static features and columns outside reactive component work. Prefer explicit features as the end state; `stockFeatures` is a kitchen-sink migration shortcut.
## Complete breaking-change map
### Construction and feature registration
| v8 | v9 |
| --------------------------------------------------------- | ------------------------------------------------------- |
| `createSolidTable(options)` | `createTable({ ...options, features })` |
| Every feature bundled | Register used `*Feature` objects with `tableFeatures()` |
| `getCoreRowModel()` option | Remove it; core is automatic |
| `getFilteredRowModel()` | `filteredRowModel: createFilteredRowModel()` |
| `getSortedRowModel()` | `sortedRowModel: createSortedRowModel()` |
| `getPaginationRowModel()` | `paginatedRowModel: createPaginatedRowModel()` |
| `getExpandedRowModel()` | `expandedRowModel: createExpandedRowModel()` |
| `getGroupedRowModel()` | `groupedRowModel: createGroupedRowModel()` |
| `getFacetedRowModel()` | `facetedRowModel: createFacetedRowModel()` |
| `getFacetedMinMaxValues()` | `facetedMinMaxValues: createFacetedMinMaxValues()` |
| `getFacetedUniqueValues()` | `facetedUniqueValues: createFacetedUniqueValues()` |
| Table/factory `sortingFns`, `filterFns`, `aggregationFns` | `sortFns`, `filterFns`, `aggregationFns` feature slots |
| Removed `rowModels: { ... }` | Direct named slots in `tableFeatures()` |
In the registry slots, register individually imported built-ins (`filterFn_includesString`, `sortFn_alphanumeric`, `aggregationFn_sum`, and so on) under their conventional keys alongside custom functions; the full `filterFns`/`sortFns`/`aggregationFns` registry objects still work but bundle every built-in.
Place each prerequisite feature before its row-model slot. Stock features are `cellSelectionFeature`, `columnFilteringFeature`, `globalFilteringFeature`, `rowSortingFeature`, `rowPaginationFeature`, `rowSelectionFeature`, `rowExpandingFeature`, `rowPinningFeature`, `columnPinningFeature`, `columnVisibilityFeature`, `columnOrderingFeature`, `columnSizingFeature`, `columnResizingFeature`, `rowAggregationFeature`, `columnGroupingFeature`, and `columnFacetingFeature`. Aggregation is independent from grouping: register `rowAggregationFeature` for aggregation APIs and add `columnGroupingFeature` only for grouped rows.
### Solid state and reactivity
| v8 | v9 |
| ------------------------------ | --------------------------------------------------------------------------- |
| `table.getState()` | `table.atoms.<slice>.get()` in tracked scopes, or broad `table.store.get()` |
| Top-level `onStateChange` | Per-slice callbacks or `table.store.subscribe()` |
| Eager signal values in options | Getters for reactive `data` and controlled state slices |
| Whole-state rendering | Narrow atom reads, `createMemo`, or `table.Subscribe` |
```tsx
const [sorting, setSorting] = createSignal<SortingState>([])
const table = createTable({
features,
columns,
get data() {
return data()
},
state: {
get sorting() {
return sorting()
},
},
onSortingChange: setSorting,
})
table.Subscribe passes atoms to its child. A Solid component child body is untracked, so read atoms inside JSX expressions or a thunk invoked by JSX:
<table.Subscribe>
{(atoms) => <span>Page {atoms.pagination.get().pageIndex + 1}</span>}
</table.Subscribe>
Use createAtom/useSelector from @tanstack/solid-store for externally owned slices. An external atom wins over state for the same slice; do not combine ownership models accidentally.
Rendering and composition
- Replace
flexRender(def, context)with<FlexRender header={header} />or<table.FlexRender cell={cell} />. - Use
tableOptions()for typed reusable option fragments. - Use
createTableHook({ features, ...defaults })for repeated conventions; it returns helpers such ascreateAppTableandcreateAppColumnHelper. - Invoke row/cell/column/header methods through their instance. Prototype methods lose
thiswhen extracted and are absent from object spread,Object.keys, and JSON. Table-instance methods are not affected.
TypeScript and helper changes
| v8 | v9 |
|---|---|
createColumnHelper<Person>() |
createColumnHelper<typeof features, Person>() |
| Plain nested column arrays | columnHelper.columns([...]) for TValue inference |
ColumnDef<TData> |
ColumnDef<TFeatures, TData, TValue> |
Column<TData>, Row<TData>, Table<TData> |
Add TFeatures first |
Cell<TData, TValue> |
Cell<TFeatures, TData, TValue> |
TableMeta<TData> / ColumnMeta<TData, TValue> |
Add TFeatures, or use per-table tableMeta / columnMeta with metaHelper() |
Global FilterFns, SortFns, AggregationFns, and FilterMeta augmentation |
filterFns, sortFns, aggregationFns, and filterMeta slots |
RowData = unknown |
Record or array row data |
Infer with typeof features; use StockFeatures only when deliberately typing stockFeatures.
Shared API and semantic changes
| v8 pinning | v9 |
|---|---|
columnPinning.left / .right |
.start / .end |
column.pin('left' | 'right') |
column.pin('start' | 'end') |
getIsPinned() === 'left' | 'right' |
'start' | 'end' |
row.getLeftVisibleCells() / getRightVisibleCells() |
getStartVisibleCells() / getEndVisibleCells() |
getLeftHeaderGroups() / getRightHeaderGroups() |
getStartHeaderGroups() / getEndHeaderGroups() |
getLeftFooterGroups() / getRightFooterGroups() |
getStartFooterGroups() / getEndFooterGroups() |
getLeftFlatHeaders() / getRightFlatHeaders() |
getStartFlatHeaders() / getEndFlatHeaders() |
getLeftLeafHeaders() / getRightLeafHeaders() |
getStartLeafHeaders() / getEndLeafHeaders() |
getLeftLeafColumns() / getRightLeafColumns() |
getStartLeafColumns() / getEndLeafColumns() |
getLeftVisibleLeafColumns() / getRightVisibleLeafColumns() |
getStartVisibleLeafColumns() / getEndVisibleLeafColumns() |
getLeftTotalSize() / getRightTotalSize() |
getStartTotalSize() / getEndTotalSize() |
column.getStart('left') |
column.getStart('start') |
column.getAfter('right') |
column.getAfter('end') |
column.getIndex('left' | 'right') |
column.getIndex('start' | 'end') |
Logical pinning does not implement DOM direction styling; use logical CSS insets. columnResizeDirection is unchanged.
| v8 | v9 |
|---|---|
Table enablePinning |
enableColumnPinning plus enableRowPinning; column-level option remains |
| Combined sizing/resizing | columnSizingFeature; add columnResizingFeature for interaction |
columnSizingInfo / onColumnSizingInfoChange |
columnResizing / onColumnResizingChange |
setColumnSizingInfo() |
setColumnResizing() |
sortingFn / sortingFns |
sortFn / sortFns |
getSortingFn() / getAutoSortingFn() |
getSortFn() / getAutoSortFn() |
SortingFn / SortingFns |
SortFn / SortFns |
row._getAllCellsByColumnId() |
row.getAllCellsByColumnId() |
table._getPinnedRows() |
getTopRows(), getCenterRows(), or getBottomRows() |
table._getFacetedRowModel() |
Public faceting APIs on the relevant column/table |
table._getFacetedMinMaxValues() |
getFacetedMinMaxValues() |
table._getFacetedUniqueValues() |
getFacetedUniqueValues() |
All other underscore-prefixed internals are removed. getIsSomeRowsSelected() and getIsSomePageRowsSelected() now mean at least one even when all are selected. Use getIsSomeRowsSelected() && !getIsAllRowsSelected() or getIsSomePageRowsSelected() && !getIsAllPageRowsSelected() for checkbox indeterminate state.
Migration procedure
- Replace
createSolidTablewithcreateTableand inventory used features, processing, state, and APIs. - Build
tableFeatures()in prerequisite order; removegetCoreRowModeland old row-model placement. - Apply all pinning, sizing, sorting, row, and selection mappings above.
- Update helpers/types with
typeof features; migrate meta and function augmentation to per-feature slots where appropriate. - Preserve Solid tracking with getters for
dataand controlled state; replacegetState()andonStateChange. - Choose internal, controlled-signal, or external-atom ownership per slice.
- Replace unbound row/cell/column/header methods and migrate rendering.
- Add
tableOptions,table.Subscribe, orcreateTableHookonly where composition or fine-grained rendering calls for them. - Type-check and exercise every enabled client- and server-side flow.
- Audit away
stockFeatureswhen production tree-shaking matters.
Final migration checklist
- Replace createSolidTable with createTable and preserve reactive inputs through getters.
- Register every used stock feature explicitly and order prerequisites before slots.
- Remove
getCoreRowModel; move all eight optional row models intotableFeatures. - Move
filterFns,sortFns,aggregationFns, andfilterMetainto feature slots. - Replace
table.getState()and onStateChange with tracked atom/store reads, per-slice callbacks, subscriptions, or external atoms. - Audit Solid tracking scopes, controlled getters, atom precedence, and reset ownership.
- Replace unbound/copied row, cell, column, and header methods.
- Apply the complete logical pinning map and CSS changes.
- Split pinning options and sizing/resizing; rename the resizing state, setter, and callback.
- Apply every sorting rename and remove each listed internal API.
- Pair some-selected with the matching all-selected predicate.
- Update TFeatures helpers/types,
columns(),StockFeatures, meta/registry slots, and RowData. - Migrate Solid FlexRender and use tableOptions/createTableHook only for repeated conventions.
- Type-check and exercise every enabled client/manual feature and LTR/RTL layout flow.
- Audit away temporary stockFeatures usage when explicit tree-shaking is intended.
Common migration failures
- Calling
createTable({ data: data() })and freezing the initial array instead of providing a getter. - Leaving row models in table options or omitting their prerequisite feature.
- Reading atoms in an untracked component child body and expecting Solid updates.
- Supplying controlled state without getter properties or per-slice callbacks.
- Destructuring prototype-backed object methods.
- Updating pinning state names but not CSS and region APIs.
- Treating changed “some selected” semantics as checkbox indeterminate state.
API discovery
Inspect node_modules/@tanstack/solid-table/dist/index.d.ts and node_modules/@tanstack/table-core/dist/index.d.ts. Compare v8 names only against the migration guide, not current main-branch assumptions.