Use this as the complete breaking-change checklist. V9 is the current API; construction, feature registration, state, rendering, and types must migrate together.
Framework prerequisite: Angular 19 or newer (@angular/core >=19).
Recommended Migration Order
Replace createAngularTable with injectTable inside an Angular injection context.
Hoist static/expensive features and columns outside the reactive initializer.
Move features, row models, and function registries into tableFeatures.
Update signal/atom state reads and FlexRender usage.
Apply every shared API and type rename below.
Treat stockFeatures as a temporary audit bridge; explicit features are the production target.
Per-slice callbacks, external atoms, or store subscription
The initializer reruns when signals read inside it change and calls setOptions; do not rebuild columns or features there.
Feature imports are cellSelectionFeature, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, rowPaginationFeature, rowSelectionFeature, rowExpandingFeature, rowPinningFeature, columnPinningFeature, columnVisibilityFeature, columnOrderingFeature, columnSizingFeature, columnResizingFeature, rowAggregationFeature, columnGroupingFeature, and columnFacetingFeature. APIs are feature-gated. Put a feature before its dependent slot in the same tableFeatures call. Aggregation is independent from grouping: register rowAggregationFeature for aggregation APIs and add columnGroupingFeature only for grouped rows.
Row-model mapping
v8 option
v9 slot and factory
getFilteredRowModel()
filteredRowModel: createFilteredRowModel() after column filtering
getSortedRowModel()
sortedRowModel: createSortedRowModel() after row sorting
getPaginationRowModel()
paginatedRowModel: createPaginatedRowModel() after pagination
getExpandedRowModel()
expandedRowModel: createExpandedRowModel() after expanding
getGroupedRowModel()
groupedRowModel: createGroupedRowModel() after grouping
getFacetedRowModel()
facetedRowModel: createFacetedRowModel() after faceting
getFacetedMinMaxValues()
facetedMinMaxValues: createFacetedMinMaxValues()
getFacetedUniqueValues()
facetedUniqueValues: createFacetedUniqueValues()
Factories take no arguments. filterFns, sortFns, and aggregationFns are sibling feature slots; register individually imported built-ins (filterFn_includesString, sortFn_alphanumeric, aggregationFn_sum) under their conventional keys. The full registry objects still work but bundle every built-in.
Angular State Migration
table.getState().sorting becomes table.atoms.sorting.get() for narrow signal-backed reads.
Use table.store.get() only for a full flat snapshot/debug output.
Derive selected slices with Angular computed; use shallow equality for recreated object/array slices when appropriate.
Controlled Angular signals are read in state and updated through matching on[State]Change callbacks; resolve value-or-function updaters.
Top-level onStateChange is removed. Use per-slice callbacks, external atoms, or table.store.subscribe for all changes.
Prefer external atoms from @tanstack/angular-store through atoms for app-owned shared slices. Never provide both an atom and state for one slice.
Treat table.baseAtoms as internal; prefer feature APIs or external atoms.
Angular Rendering and Composition
Import FlexRender/the current *flexRender directives from the adapter.
Prefer *flexRenderCell="cell; let value", *flexRenderHeader="header; let value", and *flexRenderFooter="footer; let value"; they choose the definition and context automatically.
General *flexRender supports primitives, TemplateRef, component types, and flexRenderComponent(...) wrappers.
Column render functions run in an Angular injection context and may call inject() or use signals.
Components mounted by FlexRender can call injectFlexRenderContext() for the render props.
Use flexRenderComponent(Component, { inputs, outputs, injector, bindings, directives }) for explicit component configuration; creation-time bindings/directives require the supported Angular version.
tableOptions(...) composes partial options and may omit data, columns, or features until final assembly.
createTableHook is optional for repeated application conventions; it returns injectAppTable and a feature-bound createAppColumnHelper.
Complete Shared Breaking-Change Map
Instance methods
Row, cell, column, header, and related methods now live on shared prototypes and use this. Call them on their instances. Do not destructure/pass them bare or expect them in object spread, Object.keys, or JSON. Table methods are not affected.
Other _-prefixed internals are removed, including _getPinnedRows, _getFacetedRowModel, _getFacetedMinMaxValues, and _getFacetedUniqueValues.
getIsSomeRowsSelected() and getIsSomePageRowsSelected() mean at least one, including all. Use getIsSomeRowsSelected() && !getIsAllRowsSelected() or getIsSomePageRowsSelected() && !getIsAllPageRowsSelected() for indeterminate UI.
TypeScript Migration
Most types add TFeatures first: Column<TFeatures, TData, TValue>, ColumnDef<TFeatures, TData, TValue>, Table<TFeatures, TData>, Row<TFeatures, TData>, and Cell<TFeatures, TData, TValue>.
Replace createColumnHelper<Person>() with createColumnHelper<typeof features, Person>(); use columnHelper.columns([...]) for inference.
A createTableHook column helper already binds features and needs only <Person>.
Use StockFeatures when using stockFeatures.
Existing TableMeta/ColumnMeta declaration merging must add TFeatures first. Prefer per-table meta slots using metaHelper.
Replace global FilterFns, SortFns, AggregationFns, and FilterMeta augmentation with registry slots and filterMeta; registered keys become typed string references.
RowData is now Record<string, any> | Array<any> rather than unknown.
Create it in a component/directive/service field initializer or another valid Angular injection context so ownership and cleanup bind correctly.
HIGH: Rebuilding static inputs reactively
Hoist features and columns; the initializer reruns for tracked signals.
HIGH: Leaving row models on table options
Move each row model beside its prerequisite feature in tableFeatures.
HIGH: Destructuring instance methods
Use row.getValue('name'); prototype methods require the original instance and are absent from shallow clones.
Final Checklist
createAngularTable is replaced by injectTable in injection context.
Static inputs are stable outside the signal-tracked initializer.
Features, row models, and registries are in tableFeatures; core row model is removed.
State reads use atom-backed signals or store intentionally; onStateChange is gone.
Controlled state and external atom ownership do not overlap.
FlexRender directives/helpers are migrated and imported.
Prototype methods, pinning, sizing/resizing, sorting, row, and selection changes are audited.
Helpers, types, meta, registries, and RowData use v9 shapes.
Temporary stockFeatures usage has an explicit removal plan.
API Discovery
Inspect node_modules/@tanstack/angular-table/dist/types/ for the bundled public API; do not reconstruct v9 from v8 memory.
1---2name: migrate-v8-to-v9-83description: Complete Angular v8-to-v9 migration reference: injectTable and injection context, explicit features and row-model slots, signal/atom state, FlexRender directives, type generics, prototype methods, sorting, sizing, selection, and logical pinning.4---56Use this as the complete breaking-change checklist. V9 is the current API; construction, feature registration, state, rendering, and types must migrate together.78Framework prerequisite: Angular 19 or newer (`@angular/core >=19`).910## Recommended Migration Order11121. Replace `createAngularTable` with `injectTable` inside an Angular injection context.132. Hoist static/expensive features and columns outside the reactive initializer.143. Move features, row models, and function registries into `tableFeatures`.154. Update signal/atom state reads and FlexRender usage.165. Apply every shared API and type rename below.176. Treat `stockFeatures` as a temporary audit bridge; explicit features are the production target.1819```ts20const features = tableFeatures({21 rowSortingFeature,22 sortedRowModel: createSortedRowModel(),23 sortFns: { alphanumeric: sortFn_alphanumeric },24})2526class TableCmp {27 readonly table = injectTable(() => ({28 features,29 columns,30 data: this.data(),31 }))32}33```3435## Construction and Feature Registration3637| v8 | v9 |38| ----------------------------------- | ---------------------------------------------------------- |39| `createAngularTable(() => options)` | `injectTable(() => options)` in injection context |40| All features bundled | Required `features: tableFeatures({...})` |41| `getCoreRowModel()` option | Remove; core row model is automatic |42| `get*RowModel()` table options | `create*RowModel()` slots in `tableFeatures` |43| `sortingFns` table option | `sortFns` feature slot |44| Top-level `onStateChange` | Per-slice callbacks, external atoms, or store subscription |4546The initializer reruns when signals read inside it change and calls `setOptions`; do not rebuild columns or features there.4748Feature imports are `cellSelectionFeature`, `columnFilteringFeature`, `globalFilteringFeature`, `rowSortingFeature`, `rowPaginationFeature`, `rowSelectionFeature`, `rowExpandingFeature`, `rowPinningFeature`, `columnPinningFeature`, `columnVisibilityFeature`, `columnOrderingFeature`, `columnSizingFeature`, `columnResizingFeature`, `rowAggregationFeature`, `columnGroupingFeature`, and `columnFacetingFeature`. APIs are feature-gated. Put a feature before its dependent slot in the same `tableFeatures` call. Aggregation is independent from grouping: register `rowAggregationFeature` for aggregation APIs and add `columnGroupingFeature` only for grouped rows.4950### Row-model mapping5152| v8 option | v9 slot and factory |53| -------------------------- | ------------------------------------------------------------------- |54| `getFilteredRowModel()` | `filteredRowModel: createFilteredRowModel()` after column filtering |55| `getSortedRowModel()` | `sortedRowModel: createSortedRowModel()` after row sorting |56| `getPaginationRowModel()` | `paginatedRowModel: createPaginatedRowModel()` after pagination |57| `getExpandedRowModel()` | `expandedRowModel: createExpandedRowModel()` after expanding |58| `getGroupedRowModel()` | `groupedRowModel: createGroupedRowModel()` after grouping |59| `getFacetedRowModel()` | `facetedRowModel: createFacetedRowModel()` after faceting |60| `getFacetedMinMaxValues()` | `facetedMinMaxValues: createFacetedMinMaxValues()` |61| `getFacetedUniqueValues()` | `facetedUniqueValues: createFacetedUniqueValues()` |6263Factories take no arguments. `filterFns`, `sortFns`, and `aggregationFns` are sibling feature slots; register individually imported built-ins (`filterFn_includesString`, `sortFn_alphanumeric`, `aggregationFn_sum`) under their conventional keys. The full registry objects still work but bundle every built-in.6465## Angular State Migration6667- `table.getState().sorting` becomes `table.atoms.sorting.get()` for narrow signal-backed reads.68- Use `table.store.get()` only for a full flat snapshot/debug output.69- Derive selected slices with Angular `computed`; use `shallow` equality for recreated object/array slices when appropriate.70- Controlled Angular signals are read in `state` and updated through matching `on[State]Change` callbacks; resolve value-or-function updaters.71- Top-level `onStateChange` is removed. Use per-slice callbacks, external atoms, or `table.store.subscribe` for all changes.72- Prefer external atoms from `@tanstack/angular-store` through `atoms` for app-owned shared slices. Never provide both an atom and `state` for one slice.73- Treat `table.baseAtoms` as internal; prefer feature APIs or external atoms.7475## Angular Rendering and Composition7677- Import `FlexRender`/the current `*flexRender` directives from the adapter.78- Prefer `*flexRenderCell="cell; let value"`, `*flexRenderHeader="header; let value"`, and `*flexRenderFooter="footer; let value"`; they choose the definition and context automatically.79- General `*flexRender` supports primitives, `TemplateRef`, component types, and `flexRenderComponent(...)` wrappers.80- Column render functions run in an Angular injection context and may call `inject()` or use signals.81- Components mounted by FlexRender can call `injectFlexRenderContext()` for the render props.82- Use `flexRenderComponent(Component, { inputs, outputs, injector, bindings, directives })` for explicit component configuration; creation-time `bindings`/`directives` require the supported Angular version.83- `tableOptions(...)` composes partial options and may omit data, columns, or features until final assembly.84- `createTableHook` is optional for repeated application conventions; it returns `injectAppTable` and a feature-bound `createAppColumnHelper`.8586## Complete Shared Breaking-Change Map8788### Instance methods8990Row, cell, column, header, and related methods now live on shared prototypes and use `this`. Call them on their instances. Do not destructure/pass them bare or expect them in object spread, `Object.keys`, or JSON. Table methods are not affected.9192### Logical column pinning9394V9 has no physical aliases.9596| old | new |97| -------------------------------------------------------------- | ------------------------------------------------------------- |98| `columnPinning.left` / `.right` | `.start` / `.end` |99| `column.pin('left' \| 'right')` | `column.pin('start' \| 'end')` |100| `getIsPinned() === 'left' \| 'right'` | `'start' \| 'end'` |101| `row.getLeftVisibleCells()` / `getRightVisibleCells()` | `getStartVisibleCells()` / `getEndVisibleCells()` |102| `getLeftHeaderGroups()` / `getRightHeaderGroups()` | `getStartHeaderGroups()` / `getEndHeaderGroups()` |103| `getLeftFooterGroups()` / `getRightFooterGroups()` | `getStartFooterGroups()` / `getEndFooterGroups()` |104| `getLeftFlatHeaders()` / `getRightFlatHeaders()` | `getStartFlatHeaders()` / `getEndFlatHeaders()` |105| `getLeftLeafHeaders()` / `getRightLeafHeaders()` | `getStartLeafHeaders()` / `getEndLeafHeaders()` |106| `getLeftLeafColumns()` / `getRightLeafColumns()` | `getStartLeafColumns()` / `getEndLeafColumns()` |107| `getLeftVisibleLeafColumns()` / `getRightVisibleLeafColumns()` | `getStartVisibleLeafColumns()` / `getEndVisibleLeafColumns()` |108| `getLeftTotalSize()` / `getRightTotalSize()` | `getStartTotalSize()` / `getEndTotalSize()` |109| `column.getStart('left')` | `column.getStart('start')` |110| `column.getAfter('right')` | `column.getAfter('end')` |111| `column.getIndex('left' \| 'right')` | `column.getIndex('start' \| 'end')` |112113Prefer CSS logical inset properties. Logical names do not set DOM direction. `columnResizeDirection` is unchanged.114115### Pinning, sizing, and resizing116117- `enablePinning` splits into `enableColumnPinning` and `enableRowPinning`.118- Interactive resizing requires `columnSizingFeature` and `columnResizingFeature`; fixed sizing needs only sizing.119- `columnSizingInfo` becomes `columnResizing`.120- `setColumnSizingInfo()` becomes `setColumnResizing()`.121- `onColumnSizingInfoChange` becomes `onColumnResizingChange`.122123### Sorting, rows, and selection124125| v8 | v9 |126| ------------------------------ | ----------------------------- |127| `sortingFn` | `sortFn` |128| `sortingFns` | `sortFns` |129| `getSortingFn()` | `getSortFn()` |130| `getAutoSortingFn()` | `getAutoSortFn()` |131| `SortingFn` / `SortingFns` | `SortFn` / `SortFns` |132| `row._getAllCellsByColumnId()` | `row.getAllCellsByColumnId()` |133134Other `_`-prefixed internals are removed, including `_getPinnedRows`, `_getFacetedRowModel`, `_getFacetedMinMaxValues`, and `_getFacetedUniqueValues`.135136`getIsSomeRowsSelected()` and `getIsSomePageRowsSelected()` mean at least one, including all. Use `getIsSomeRowsSelected() && !getIsAllRowsSelected()` or `getIsSomePageRowsSelected() && !getIsAllPageRowsSelected()` for indeterminate UI.137138## TypeScript Migration139140- Most types add `TFeatures` first: `Column<TFeatures, TData, TValue>`, `ColumnDef<TFeatures, TData, TValue>`, `Table<TFeatures, TData>`, `Row<TFeatures, TData>`, and `Cell<TFeatures, TData, TValue>`.141- Replace `createColumnHelper<Person>()` with `createColumnHelper<typeof features, Person>()`; use `columnHelper.columns([...])` for inference.142- A `createTableHook` column helper already binds features and needs only `<Person>`.143- Use `StockFeatures` when using `stockFeatures`.144- Existing `TableMeta`/`ColumnMeta` declaration merging must add `TFeatures` first. Prefer per-table meta slots using `metaHelper`.145- Replace global `FilterFns`, `SortFns`, `AggregationFns`, and `FilterMeta` augmentation with registry slots and `filterMeta`; registered keys become typed string references.146- `RowData` is now `Record<string, any> | Array<any>` rather than `unknown`.147148## Common Migration Failures149150### CRITICAL: Calling injectTable outside injection context151152Create it in a component/directive/service field initializer or another valid Angular injection context so ownership and cleanup bind correctly.153154### HIGH: Rebuilding static inputs reactively155156Hoist `features` and `columns`; the initializer reruns for tracked signals.157158### HIGH: Leaving row models on table options159160Move each row model beside its prerequisite feature in `tableFeatures`.161162### HIGH: Destructuring instance methods163164Use `row.getValue('name')`; prototype methods require the original instance and are absent from shallow clones.165166## Final Checklist167168- [ ] `createAngularTable` is replaced by `injectTable` in injection context.169- [ ] Static inputs are stable outside the signal-tracked initializer.170- [ ] Features, row models, and registries are in `tableFeatures`; core row model is removed.171- [ ] State reads use atom-backed signals or store intentionally; `onStateChange` is gone.172- [ ] Controlled state and external atom ownership do not overlap.173- [ ] FlexRender directives/helpers are migrated and imported.174- [ ] Prototype methods, pinning, sizing/resizing, sorting, row, and selection changes are audited.175- [ ] Helpers, types, meta, registries, and `RowData` use v9 shapes.176- [ ] Temporary `stockFeatures` usage has an explicit removal plan.177178## API Discovery179180Inspect `node_modules/@tanstack/angular-table/dist/types/` for the bundled public API; do not reconstruct v9 from v8 memory.
Run npx skillmds@latest add tanstack/migrate-v8-to-v9-8 in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Complete Angular v8-to-v9 migration reference: injectTable and injection context, explicit features and row-model slots, signal/atom state, FlexRender directives, type generics, prototype methods, sorting, sizing, selection, and logical pinning. It is listed under AI & ML on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
TanStack (@tanstack) published this skill. Their other Agent Skills are listed on their SkillMD profile.