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.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
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 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.