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: tanstack-angular-table-migrate-v8-to-v93description: 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.4license: MIT5---67Use this as the complete breaking-change checklist. V9 is the current API; construction, feature registration, state, rendering, and types must migrate together.89Framework prerequisite: Angular 19 or newer (`@angular/core >=19`).1011## Recommended Migration Order12131. Replace `createAngularTable` with `injectTable` inside an Angular injection context.142. Hoist static/expensive features and columns outside the reactive initializer.153. Move features, row models, and function registries into `tableFeatures`.164. Update signal/atom state reads and FlexRender usage.175. Apply every shared API and type rename below.186. Treat `stockFeatures` as a temporary audit bridge; explicit features are the production target.1920```ts21const features = tableFeatures({22 rowSortingFeature,23 sortedRowModel: createSortedRowModel(),24 sortFns: { alphanumeric: sortFn_alphanumeric },25})2627class TableCmp {28 readonly table = injectTable(() => ({29 features,30 columns,31 data: this.data(),32 }))33}34```3536## Construction and Feature Registration3738| v8 | v9 |39| ----------------------------------- | ---------------------------------------------------------- |40| `createAngularTable(() => options)` | `injectTable(() => options)` in injection context |41| All features bundled | Required `features: tableFeatures({...})` |42| `getCoreRowModel()` option | Remove; core row model is automatic |43| `get*RowModel()` table options | `create*RowModel()` slots in `tableFeatures` |44| `sortingFns` table option | `sortFns` feature slot |45| Top-level `onStateChange` | Per-slice callbacks, external atoms, or store subscription |4647The initializer reruns when signals read inside it change and calls `setOptions`; do not rebuild columns or features there.4849Feature 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.5051### Row-model mapping5253| v8 option | v9 slot and factory |54| -------------------------- | ------------------------------------------------------------------- |55| `getFilteredRowModel()` | `filteredRowModel: createFilteredRowModel()` after column filtering |56| `getSortedRowModel()` | `sortedRowModel: createSortedRowModel()` after row sorting |57| `getPaginationRowModel()` | `paginatedRowModel: createPaginatedRowModel()` after pagination |58| `getExpandedRowModel()` | `expandedRowModel: createExpandedRowModel()` after expanding |59| `getGroupedRowModel()` | `groupedRowModel: createGroupedRowModel()` after grouping |60| `getFacetedRowModel()` | `facetedRowModel: createFacetedRowModel()` after faceting |61| `getFacetedMinMaxValues()` | `facetedMinMaxValues: createFacetedMinMaxValues()` |62| `getFacetedUniqueValues()` | `facetedUniqueValues: createFacetedUniqueValues()` |6364Factories 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.6566## Angular State Migration6768- `table.getState().sorting` becomes `table.atoms.sorting.get()` for narrow signal-backed reads.69- Use `table.store.get()` only for a full flat snapshot/debug output.70- Derive selected slices with Angular `computed`; use `shallow` equality for recreated object/array slices when appropriate.71- Controlled Angular signals are read in `state` and updated through matching `on[State]Change` callbacks; resolve value-or-function updaters.72- Top-level `onStateChange` is removed. Use per-slice callbacks, external atoms, or `table.store.subscribe` for all changes.73- Prefer external atoms from `@tanstack/angular-store` through `atoms` for app-owned shared slices. Never provide both an atom and `state` for one slice.74- Treat `table.baseAtoms` as internal; prefer feature APIs or external atoms.7576## Angular Rendering and Composition7778- Import `FlexRender`/the current `*flexRender` directives from the adapter.79- Prefer `*flexRenderCell="cell; let value"`, `*flexRenderHeader="header; let value"`, and `*flexRenderFooter="footer; let value"`; they choose the definition and context automatically.80- General `*flexRender` supports primitives, `TemplateRef`, component types, and `flexRenderComponent(...)` wrappers.81- Column render functions run in an Angular injection context and may call `inject()` or use signals.82- Components mounted by FlexRender can call `injectFlexRenderContext()` for the render props.83- Use `flexRenderComponent(Component, { inputs, outputs, injector, bindings, directives })` for explicit component configuration; creation-time `bindings`/`directives` require the supported Angular version.84- `tableOptions(...)` composes partial options and may omit data, columns, or features until final assembly.85- `createTableHook` is optional for repeated application conventions; it returns `injectAppTable` and a feature-bound `createAppColumnHelper`.8687## Complete Shared Breaking-Change Map8889### Instance methods9091Row, 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.9293### Logical column pinning9495V9 has no physical aliases.9697| old | new |98| -------------------------------------------------------------- | ------------------------------------------------------------- |99| `columnPinning.left` / `.right` | `.start` / `.end` |100| `column.pin('left' \| 'right')` | `column.pin('start' \| 'end')` |101| `getIsPinned() === 'left' \| 'right'` | `'start' \| 'end'` |102| `row.getLeftVisibleCells()` / `getRightVisibleCells()` | `getStartVisibleCells()` / `getEndVisibleCells()` |103| `getLeftHeaderGroups()` / `getRightHeaderGroups()` | `getStartHeaderGroups()` / `getEndHeaderGroups()` |104| `getLeftFooterGroups()` / `getRightFooterGroups()` | `getStartFooterGroups()` / `getEndFooterGroups()` |105| `getLeftFlatHeaders()` / `getRightFlatHeaders()` | `getStartFlatHeaders()` / `getEndFlatHeaders()` |106| `getLeftLeafHeaders()` / `getRightLeafHeaders()` | `getStartLeafHeaders()` / `getEndLeafHeaders()` |107| `getLeftLeafColumns()` / `getRightLeafColumns()` | `getStartLeafColumns()` / `getEndLeafColumns()` |108| `getLeftVisibleLeafColumns()` / `getRightVisibleLeafColumns()` | `getStartVisibleLeafColumns()` / `getEndVisibleLeafColumns()` |109| `getLeftTotalSize()` / `getRightTotalSize()` | `getStartTotalSize()` / `getEndTotalSize()` |110| `column.getStart('left')` | `column.getStart('start')` |111| `column.getAfter('right')` | `column.getAfter('end')` |112| `column.getIndex('left' \| 'right')` | `column.getIndex('start' \| 'end')` |113114Prefer CSS logical inset properties. Logical names do not set DOM direction. `columnResizeDirection` is unchanged.115116### Pinning, sizing, and resizing117118- `enablePinning` splits into `enableColumnPinning` and `enableRowPinning`.119- Interactive resizing requires `columnSizingFeature` and `columnResizingFeature`; fixed sizing needs only sizing.120- `columnSizingInfo` becomes `columnResizing`.121- `setColumnSizingInfo()` becomes `setColumnResizing()`.122- `onColumnSizingInfoChange` becomes `onColumnResizingChange`.123124### Sorting, rows, and selection125126| v8 | v9 |127| ------------------------------ | ----------------------------- |128| `sortingFn` | `sortFn` |129| `sortingFns` | `sortFns` |130| `getSortingFn()` | `getSortFn()` |131| `getAutoSortingFn()` | `getAutoSortFn()` |132| `SortingFn` / `SortingFns` | `SortFn` / `SortFns` |133| `row._getAllCellsByColumnId()` | `row.getAllCellsByColumnId()` |134135Other `_`-prefixed internals are removed, including `_getPinnedRows`, `_getFacetedRowModel`, `_getFacetedMinMaxValues`, and `_getFacetedUniqueValues`.136137`getIsSomeRowsSelected()` and `getIsSomePageRowsSelected()` mean at least one, including all. Use `getIsSomeRowsSelected() && !getIsAllRowsSelected()` or `getIsSomePageRowsSelected() && !getIsAllPageRowsSelected()` for indeterminate UI.138139## TypeScript Migration140141- Most types add `TFeatures` first: `Column<TFeatures, TData, TValue>`, `ColumnDef<TFeatures, TData, TValue>`, `Table<TFeatures, TData>`, `Row<TFeatures, TData>`, and `Cell<TFeatures, TData, TValue>`.142- Replace `createColumnHelper<Person>()` with `createColumnHelper<typeof features, Person>()`; use `columnHelper.columns([...])` for inference.143- A `createTableHook` column helper already binds features and needs only `<Person>`.144- Use `StockFeatures` when using `stockFeatures`.145- Existing `TableMeta`/`ColumnMeta` declaration merging must add `TFeatures` first. Prefer per-table meta slots using `metaHelper`.146- Replace global `FilterFns`, `SortFns`, `AggregationFns`, and `FilterMeta` augmentation with registry slots and `filterMeta`; registered keys become typed string references.147- `RowData` is now `Record<string, any> | Array<any>` rather than `unknown`.148149## Common Migration Failures150151### CRITICAL: Calling injectTable outside injection context152153Create it in a component/directive/service field initializer or another valid Angular injection context so ownership and cleanup bind correctly.154155### HIGH: Rebuilding static inputs reactively156157Hoist `features` and `columns`; the initializer reruns for tracked signals.158159### HIGH: Leaving row models on table options160161Move each row model beside its prerequisite feature in `tableFeatures`.162163### HIGH: Destructuring instance methods164165Use `row.getValue('name')`; prototype methods require the original instance and are absent from shallow clones.166167## Final Checklist168169- [ ] `createAngularTable` is replaced by `injectTable` in injection context.170- [ ] Static inputs are stable outside the signal-tracked initializer.171- [ ] Features, row models, and registries are in `tableFeatures`; core row model is removed.172- [ ] State reads use atom-backed signals or store intentionally; `onStateChange` is gone.173- [ ] Controlled state and external atom ownership do not overlap.174- [ ] FlexRender directives/helpers are migrated and imported.175- [ ] Prototype methods, pinning, sizing/resizing, sorting, row, and selection changes are audited.176- [ ] Helpers, types, meta, registries, and `RowData` use v9 shapes.177- [ ] Temporary `stockFeatures` usage has an explicit removal plan.178179## API Discovery180181Inspect `node_modules/@tanstack/angular-table/dist/types/` for the bundled public API; do not reconstruct v9 from v8 memory.
Run npx skillmds@latest add lukasa1993/tanstack-angular-table-migrate-v8-to-v9 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. This skill is licensed under MIT.
lukasa1993 (@lukasa1993) published this skill. Their other Agent Skills are listed on their SkillMD profile.