@tanstack/angular-table 9.0.0-beta.2 → 9.0.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,13 +2,15 @@
2
2
  name: angular/migrate-v8-to-v9
3
3
  description: >
4
4
  Mechanical v8 → v9 migration for `@tanstack/angular-table`: `createAngularTable` →
5
- `injectTable`, `get*RowModel()` options → `rowModels` factories with explicit `*Fns`,
6
- required `features` via `tableFeatures()`, state access via `table.atoms.<slice>.get()`
7
- or `table.state` instead of `table.getState()`, `createColumnHelper<TFeatures, TData>()` generic-order flip, every
8
- type now requires `TFeatures`, `enablePinning` split into `enableColumnPinning` /
9
- `enableRowPinning`, `sortingFn` `sortFn` rename pile, `ColumnSizingInfo` `ColumnResizing`
10
- split, removal of `_`-prefixed internals, signal-backed atoms replacing v8 memoized accessors,
11
- and structural-directive rendering replacing v8 component-based rendering.
5
+ `injectTable`, `get*RowModel()` options → row-model factories on the `features` object (no
6
+ separate `rowModels` option; fn registries like `filterFns`/`sortFns` are also slots on
7
+ `features`), required `features` via `tableFeatures()`, state access via
8
+ `table.atoms.<slice>.get()` or `table.state` instead of `table.getState()`,
9
+ `createColumnHelper<TFeatures, TData>()` generic-order flip, every type now requires `TFeatures`,
10
+ `enablePinning` split into `enableColumnPinning` / `enableRowPinning`, `sortingFn` `sortFn`
11
+ rename pile, `ColumnSizingInfo` `ColumnResizing` split, removal of `_`-prefixed internals,
12
+ signal-backed atoms replacing v8 memoized accessors, and structural-directive rendering replacing
13
+ v8 component-based rendering.
12
14
  type: lifecycle
13
15
  library: tanstack-table
14
16
  framework: angular
@@ -27,9 +29,9 @@ sources:
27
29
  # Migrate from TanStack Table v8 to v9 (Angular)
28
30
 
29
31
  > **Angular does not ship a legacy v8 API in v9** (unlike React's
30
- > `useLegacyTable`). You migrate directly to v9's `injectTable` + `features` +
31
- > `rowModels` shape. There is no incremental in-place adapter the public
32
- > entrypoint name itself changes.
32
+ > `useLegacyTable`). You migrate directly to v9's `injectTable` + `features`
33
+ > shape (row-model factories and fn registries now live on the `features` object).
34
+ > There is no incremental in-place adapter — the public entrypoint name itself changes.
33
35
 
34
36
  This skill is a mechanical translation table. Work through it top-to-bottom.
35
37
 
@@ -58,7 +60,6 @@ const features = tableFeatures({}) // empty is valid; core row model is automati
58
60
 
59
61
  const v9Table = injectTable(() => ({
60
62
  features,
61
- rowModels: {},
62
63
  columns,
63
64
  data: data(),
64
65
  }))
@@ -66,14 +67,16 @@ const v9Table = injectTable(() => ({
66
67
 
67
68
  Key behavioral change: **the `injectTable` initializer re-runs when signals
68
69
  inside it change**, then the adapter calls `table.setOptions({ ...prev, ...new })`.
69
- Move stable values (`columns`, `features`, `rowModels`) **outside** the
70
- initializer so they aren't recreated on every data update.
70
+ Move stable values (`columns`, `features`) **outside** the initializer so they
71
+ aren't recreated on every data update.
71
72
 
72
73
  ---
73
74
 
74
- ## 2. Required new options: `features` + `rowModels`
75
+ ## 2. Required new option: `features`
75
76
 
76
- v9 is opt-in for every feature. **Both options are required.**
77
+ v9 is opt-in for every feature. `features` is required. Row-model factories and
78
+ fn registries are now **slots on the features object** — there is no separate
79
+ `rowModels` option.
77
80
 
78
81
  ```ts
79
82
  // v8 — features were bundled, row models added piecewise
@@ -106,15 +109,15 @@ const features = tableFeatures({
106
109
  columnFilteringFeature,
107
110
  rowSortingFeature,
108
111
  rowPaginationFeature,
112
+ filteredRowModel: createFilteredRowModel(), // factory is now a features slot
113
+ sortedRowModel: createSortedRowModel(),
114
+ paginatedRowModel: createPaginatedRowModel(),
115
+ filterFns, // fn registry is also a features slot
116
+ sortFns, // note rename: sortingFns → sortFns
109
117
  })
110
118
 
111
119
  injectTable(() => ({
112
120
  features,
113
- rowModels: {
114
- filteredRowModel: createFilteredRowModel(filterFns), // fns are PARAMETERS now
115
- sortedRowModel: createSortedRowModel(sortFns),
116
- paginatedRowModel: createPaginatedRowModel(),
117
- },
118
121
  columns,
119
122
  data: data(),
120
123
  }))
@@ -167,7 +170,6 @@ readonly pagination = signal<PaginationState>({ pageIndex: 0, pageSize: 10 })
167
170
 
168
171
  readonly table = injectTable(() => ({
169
172
  features,
170
- rowModels: { /* … */ },
171
173
  columns,
172
174
  data: this.data(),
173
175
  state: {
@@ -302,10 +304,10 @@ v8 backed reactivity with manual memoized getters. v9's adapter
302
304
 
303
305
  - [ ] Replace `createAngularTable` import + call with `injectTable`.
304
306
  - [ ] Add `features: tableFeatures({...})` (or `stockFeatures`) — required.
305
- - [ ] Convert every `get*RowModel()` option to a `rowModels.<slot>` entry with
306
- the matching `create*RowModel(...)` factory.
307
- - [ ] Add `filterFns` / `sortFns` / `aggregationFns` as **factory parameters**
308
- where needed.
307
+ - [ ] Convert every `get*RowModel()` option to a slot on the `features` object
308
+ using the matching `create*RowModel()` factory.
309
+ - [ ] Move `filterFns` / `sortFns` / `aggregationFns` fn registries to slots
310
+ on the `features` object (not as factory parameters).
309
311
  - [ ] Update `createColumnHelper<Person>()` → `createColumnHelper<typeof features, Person>()`.
310
312
  - [ ] Update every `ColumnDef<Person>` / `Cell<Person, X>` etc. to include
311
313
  `TFeatures`.
@@ -314,8 +316,7 @@ v8 backed reactivity with manual memoized getters. v9's adapter
314
316
  - [ ] Remove any usage of the v8 single `onStateChange` — split into per-slice
315
317
  `on[State]Change`.
316
318
  - [ ] In `on[State]Change` callbacks, handle both value and updater-fn shapes.
317
- - [ ] Move `columns`, `features`, `rowModels` **outside** the `injectTable`
318
- initializer.
319
+ - [ ] Move `columns` and `features` **outside** the `injectTable` initializer.
319
320
  - [ ] Switch any `flexRender` long-form to `*flexRenderCell` / `*flexRenderHeader` /
320
321
  `*flexRenderFooter` shorthand where applicable.
321
322
  - [ ] Where you need component rendering with explicit options, switch wrapper
@@ -326,8 +327,9 @@ v8 backed reactivity with manual memoized getters. v9's adapter
326
327
  - [ ] Rename `sortingFn` → `sortFn`, `getSortingFn` → `getSortFn`,
327
328
  `sortingFns` → `sortFns`, `SortingFn` → `SortFn`.
328
329
  - [ ] Replace `columnSizingInfo` state / setters / change handler with the
329
- `columnResizing` equivalents; add `columnResizingFeature` to `features`
330
- if you actually drag-resize.
330
+ `columnResizing` equivalents; add `columnSizingFeature` and
331
+ `columnResizingFeature` to `features` if you actually drag-resize
332
+ (`columnResizingFeature` requires `columnSizingFeature`).
331
333
  - [ ] Replace `enablePinning` with `enableColumnPinning` / `enableRowPinning`.
332
334
  - [ ] Update `ColumnMeta` module augmentation to include the `TFeatures`
333
335
  generic.
@@ -342,9 +344,9 @@ v8 backed reactivity with manual memoized getters. v9's adapter
342
344
 
343
345
  ### 1. (CRITICAL) Leaving `getCoreRowModel()` / `getSortedRowModel()` / etc. in v9 options
344
346
 
345
- These options don't exist anymore. They become `rowModels` entries with
346
- factory functions. The TypeScript error is loud but agents sometimes silence
347
- it with `as any` — don't.
347
+ These options don't exist anymore. The equivalent factories become slots on the
348
+ `features` object (e.g. `sortedRowModel: createSortedRowModel()`). The TypeScript
349
+ error is loud but agents sometimes silence it with `as any` — don't.
348
350
 
349
351
  ### 2. (CRITICAL) Reaching for `createAngularTable` from v8 muscle memory
350
352
 
@@ -356,14 +358,14 @@ it must run from a class field, constructor, or
356
358
 
357
359
  ```ts
358
360
  // ❌ filtering enabled, but no filtered row model — UI changes, rows don't filter
359
- features: tableFeatures({ columnFilteringFeature })
360
- rowModels: {
361
- } // missing filteredRowModel
361
+ const features = tableFeatures({ columnFilteringFeature }) // missing filteredRowModel
362
362
 
363
363
  // ✅
364
- rowModels: {
365
- filteredRowModel: createFilteredRowModel(filterFns)
366
- }
364
+ const features = tableFeatures({
365
+ columnFilteringFeature,
366
+ filteredRowModel: createFilteredRowModel(),
367
+ filterFns,
368
+ })
367
369
  ```
368
370
 
369
371
  Same for sorting, pagination, expanding, grouping, faceting. Selection,
@@ -390,11 +392,12 @@ to default sort.
390
392
  const columnHelper = createColumnHelper<Person>()
391
393
  ```
392
394
 
393
- ### 7. (HIGH) Putting `features` / `columns` / row-model factories inside the `injectTable` initializer
395
+ ### 7. (HIGH) Putting `features` / `columns` inside the `injectTable` initializer
394
396
 
395
397
  The v8 mental model was "build columns inside the hook". v9's
396
398
  `injectTable` initializer re-runs on every signal read change — keep heavy
397
- literals outside.
399
+ literals outside. Because row-model factories and fn registries live on the
400
+ `features` object, keeping `features` stable at module scope covers all of them.
398
401
 
399
402
  Lower-severity failure modes (MEDIUM/LOW: `stockFeatures` cleanup, `enablePinning`
400
403
  removal, `columnSizingInfo` rename, single-handler porting, hand-rolled
@@ -8,17 +8,21 @@ exhaustive lookup.
8
8
 
9
9
  ## Row-model migration table
10
10
 
11
- | v8 option | v9 `rowModels` key | v9 factory |
12
- | -------------------------- | --------------------- | --------------------------------------- |
13
- | `getCoreRowModel()` | (automatic) | — |
14
- | `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` |
15
- | `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` |
16
- | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` |
17
- | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` |
18
- | `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` |
19
- | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` |
20
- | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` |
21
- | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` |
11
+ Row-model factories and fn registries are now **slots on the `features` object**,
12
+ not a separate `rowModels` option. Factory calls no longer take fn arguments;
13
+ pass the fn map as a sibling slot instead.
14
+
15
+ | v8 option | v9 `features` slot key | v9 factory call | v9 fn registry slot |
16
+ | -------------------------- | ---------------------- | ----------------------------- | ------------------- |
17
+ | `getCoreRowModel()` | (automatic) | — | — |
18
+ | `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel()` | `filterFns` |
19
+ | `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel()` | `sortFns` |
20
+ | `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` | — |
21
+ | `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` | — |
22
+ | `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel()` | `aggregationFns` |
23
+ | `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` | — |
24
+ | `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` | — |
25
+ | `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` | — |
22
26
 
23
27
  ## Feature registration table
24
28
 
@@ -116,7 +120,7 @@ declare module '@tanstack/angular-table' {
116
120
  | `SortingFns` interface | `SortFns` |
117
121
  | `sortingFns` (built-in fn registry) | `sortFns` |
118
122
 
119
- Find-and-replace, then update `createSortedRowModel(sortFns)`.
123
+ Find-and-replace, then verify `sortFns` is a slot on `features` alongside `sortedRowModel: createSortedRowModel()`.
120
124
 
121
125
  ---
122
126
 
@@ -171,13 +175,16 @@ during migration if you have shared base config:
171
175
  import { tableOptions, tableFeatures, rowSortingFeature } from '@tanstack/angular-table'
172
176
 
173
177
  const baseOptions = tableOptions({
174
- features: tableFeatures({ rowSortingFeature }),
178
+ features: tableFeatures({
179
+ rowSortingFeature,
180
+ sortedRowModel: createSortedRowModel(),
181
+ sortFns,
182
+ }),
175
183
  debugTable: isDevMode(),
176
184
  })
177
185
 
178
186
  readonly table = injectTable(() => ({
179
187
  ...baseOptions,
180
- rowModels: { /* … */ },
181
188
  columns: this.columns,
182
189
  data: this.data(),
183
190
  }))
@@ -2,14 +2,14 @@
2
2
  name: angular/production-readiness
3
3
  description: >
4
4
  Ship-ready optimizations for Angular Table v9: register only the `features` you actually use
5
- (tree-shake the bundle); keep `columns` / `features` / `rowModels` / feature-fn maps as
6
- stable references OUTSIDE the `injectTable` initializer; pass only the `*Fns` your data needs
7
- to `createSortedRowModel` / `createFilteredRowModel` / `createGroupedRowModel`; use
8
- `ChangeDetectionStrategy.OnPush`; lean on signal-backed atoms (`table.atoms.<slice>.get()`)
9
- instead of broad `table.state` reads where granularity matters; use `{ equal: shallow }`
10
- on object/array `computed` selectors; set `getRowId` for stable identity; track by `id` in
11
- every `@for`; defer cell components with `flexRenderComponent` only when you need its options;
12
- scope DI tokens via `[tanStackTable*]` directives to kill prop drilling.
5
+ (tree-shake the bundle); keep `columns` / `features` (which carries row-model factories and
6
+ fn registries) as stable references OUTSIDE the `injectTable` initializer; pass only the `*Fns`
7
+ your data needs as slots on the features object; use `ChangeDetectionStrategy.OnPush`; lean on
8
+ signal-backed atoms (`table.atoms.<slice>.get()`) instead of broad `table.state` reads where
9
+ granularity matters; use `{ equal: shallow }` on object/array `computed` selectors; set
10
+ `getRowId` for stable identity; track by `id` in every `@for`; defer cell components with
11
+ `flexRenderComponent` only when you need its options; scope DI tokens via `[tanStackTable*]`
12
+ directives to kill prop drilling.
13
13
  type: lifecycle
14
14
  library: tanstack-table
15
15
  framework: angular
@@ -59,10 +59,11 @@ Use `stockFeatures` to bootstrap during a v8 → v9 migration, then **come back
59
59
  and curate**. The bundle wins only land once you do.
60
60
 
61
61
  The same applies to feature-fn registries — pass only the `*Fns` your data
62
- needs:
62
+ needs as slots on the features object:
63
63
 
64
64
  ```ts
65
65
  import {
66
+ tableFeatures,
66
67
  createSortedRowModel,
67
68
  createFilteredRowModel,
68
69
  sortFns,
@@ -70,16 +71,20 @@ import {
70
71
  } from '@tanstack/angular-table'
71
72
 
72
73
  // ❌ pulls in every built-in sort + filter fn
73
- rowModels: {
74
- sortedRowModel: createSortedRowModel(sortFns),
75
- filteredRowModel: createFilteredRowModel(filterFns),
76
- }
74
+ const features = tableFeatures({
75
+ sortedRowModel: createSortedRowModel(),
76
+ filteredRowModel: createFilteredRowModel(),
77
+ sortFns,
78
+ filterFns,
79
+ })
77
80
 
78
81
  // ✅ only what you use
79
- rowModels: {
80
- sortedRowModel: createSortedRowModel({ basic: sortFns.basic, datetime: sortFns.datetime }),
81
- filteredRowModel: createFilteredRowModel({ includesString: filterFns.includesString }),
82
- }
82
+ const features = tableFeatures({
83
+ sortedRowModel: createSortedRowModel(),
84
+ filteredRowModel: createFilteredRowModel(),
85
+ sortFns: { basic: sortFns.basic, datetime: sortFns.datetime },
86
+ filterFns: { includesString: filterFns.includesString },
87
+ })
83
88
  ```
84
89
 
85
90
  Same logic for `aggregationFns` if you use grouping.
@@ -91,30 +96,28 @@ Same logic for `aggregationFns` if you use grouping.
91
96
  `injectTable(() => ({...}))` **re-runs the initializer every time a signal read
92
97
  inside it changes** and then calls `table.setOptions({ ...prev, ...new })`.
93
98
  Anything you create inside the initializer is recreated on every signal
94
- change.
99
+ change. Because row-model factories and fn registries live on the `features`
100
+ object, keeping `features` stable at module scope covers all of them.
95
101
 
96
102
  ```ts
97
- // ❌ columns / features / rowModels / feature fns recreated on every data() change
103
+ // ❌ columns / features recreated on every data() change
98
104
  @Component({...})
99
105
  export class App {
100
106
  readonly table = injectTable(() => ({
101
- features: tableFeatures({ rowSortingFeature }), // new ref each run
102
- rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, // ← new ref each run
103
- columns: [/* … */], // ← new ref each run
107
+ features: tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns }),
108
+ columns: [/* */],
104
109
  data: this.data(),
105
110
  }))
106
111
  }
107
112
 
108
113
  // ✅ stable references outside; only reactive reads inside
109
- const features = tableFeatures({ rowSortingFeature })
110
- const rowModels = { sortedRowModel: createSortedRowModel(sortFns) }
114
+ const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns })
111
115
  const columns: Array<ColumnDef<typeof features, Person>> = [/* … */]
112
116
 
113
117
  @Component({...})
114
118
  export class App {
115
119
  readonly table = injectTable(() => ({
116
120
  features,
117
- rowModels,
118
121
  columns,
119
122
  data: this.data(),
120
123
  }))
@@ -125,7 +128,7 @@ Same rule for the controlled-state pattern — keep `state: { pagination: this.p
125
128
  inside the initializer, but keep the signal definitions on the class.
126
129
 
127
130
  For shared infrastructure across multiple tables, `createTableHook(...)` lets
128
- you define `features` / `rowModels` / default options once at module scope.
131
+ you define `features` / default options once at module scope.
129
132
 
130
133
  ---
131
134
 
@@ -337,11 +340,11 @@ the wiring cost. Pick exactly one source of truth per slice (see
337
340
  ## 13. Quick wins checklist
338
341
 
339
342
  - [ ] `features` listed explicitly (no `stockFeatures` in production).
340
- - [ ] `*Fns` registries passed only what you use to
341
- `createSortedRowModel` / `createFilteredRowModel` /
342
- `createGroupedRowModel`.
343
- - [ ] `columns`, `features`, `rowModels`, feature fns are at module scope
344
- or stable class fields never inside the `injectTable` initializer.
343
+ - [ ] `*Fns` registry slots on `features` pass only what you use (not the full
344
+ `sortFns` / `filterFns` / `aggregationFns` spread when you can narrow it).
345
+ - [ ] `columns` and `features` are at module scope or stable class fields —
346
+ never inside the `injectTable` initializer (factories and fn registries
347
+ live on `features`, so this covers them too).
345
348
  - [ ] Component is `ChangeDetectionStrategy.OnPush`.
346
349
  - [ ] `getRowId` set when rows have a stable primary key.
347
350
  - [ ] All `@for` blocks track by `id`.
@@ -366,13 +369,12 @@ the wiring cost. Pick exactly one source of truth per slice (see
366
369
  calls this out — `stockFeatures` is a v8 → v9 bootstrap, not a production
367
370
  end-state.
368
371
 
369
- ### 2. (CRITICAL) Recreating `columns` / `features` / `rowModels` inside the
370
-
371
- `injectTable` initializer
372
+ ### 2. (CRITICAL) Recreating `columns` / `features` inside the `injectTable` initializer
372
373
 
373
- The initializer re-runs on every signal read change. New `columns` reference
374
+ The initializer re-runs on every signal read change. A new `columns` reference
374
375
  triggers full column-model rebuilds — for big tables this is visibly slow.
375
- Module-scope it.
376
+ Module-scope both `columns` and `features` (which carries the factories and fn
377
+ registries).
376
378
 
377
379
  ### 3. (CRITICAL) Reimplementing what the table already does
378
380
 
@@ -386,7 +388,8 @@ Symptoms:
386
388
 
387
389
  All of these are far slower than the built-in row models (which memoize and
388
390
  short-circuit) and ship more code. Use `table.setSorting(...)`,
389
- `table.setColumnFilters(...)`, the registered `rowModels` factories.
391
+ `table.setColumnFilters(...)`, and the row-model factories registered on the
392
+ `features` object.
390
393
 
391
394
  ### 4. (HIGH) `OnPush` not set
392
395
 
@@ -36,7 +36,7 @@ sources:
36
36
 
37
37
  ---
38
38
 
39
- ## 1. Prerequisites — `features` and `rowModels` decide what state exists
39
+ ## 1. Prerequisites — `features` decides what state exists
40
40
 
41
41
  In v9, **a state slice only exists if its feature is registered in `features`**.
42
42
  This is the #1 v9-specific gotcha and the root cause of many "missing API"
@@ -57,14 +57,13 @@ import {
57
57
  const features = tableFeatures({
58
58
  rowPaginationFeature,
59
59
  rowSortingFeature,
60
+ paginatedRowModel: createPaginatedRowModel(),
61
+ sortedRowModel: createSortedRowModel(),
62
+ sortFns,
60
63
  })
61
64
 
62
65
  readonly table = injectTable(() => ({
63
66
  features,
64
- rowModels: {
65
- paginatedRowModel: createPaginatedRowModel(),
66
- sortedRowModel: createSortedRowModel(sortFns),
67
- },
68
67
  columns,
69
68
  data: this.data(),
70
69
  }))
@@ -91,7 +90,6 @@ run inside an Angular injection context (a component constructor / class field).
91
90
  ```ts
92
91
  readonly table = injectTable(() => ({
93
92
  features,
94
- rowModels: {},
95
93
  columns,
96
94
  data: this.data(),
97
95
  }))
@@ -105,25 +103,23 @@ That means:
105
103
 
106
104
  - **Reactive values that should re-sync the table** (`this.data()`, controlled
107
105
  state signals) go _inside_ the initializer.
108
- - **Stable references** (`columns`, `features`, `rowModels`, feature-fn maps)
109
- go _outside_ — or you'll recreate the column model on every data update.
106
+ - **Stable references** (`columns`, `features`) go _outside_ — or you'll
107
+ recreate the column model on every data update.
110
108
 
111
109
  ```ts
112
110
  // ❌ WRONG — columns + features recreated on every data change
113
111
  readonly table = injectTable(() => ({
114
- features: tableFeatures({ rowSortingFeature }), // new reference each run
115
- rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, // ditto
116
- columns: [/* … */], // ditto
112
+ features: tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns }),
113
+ columns: [/* */],
117
114
  data: this.data(),
118
115
  }))
119
116
 
120
117
  // ✅ Stable references outside, signal reads inside
121
- const features = tableFeatures({ rowSortingFeature })
118
+ const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns })
122
119
  const columns: Array<ColumnDef<typeof features, Person>> = [/* … */]
123
120
 
124
121
  readonly table = injectTable(() => ({
125
122
  features,
126
- rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
127
123
  columns,
128
124
  data: this.data(), // ← only the signal read should be inside
129
125
  }))
@@ -230,7 +226,6 @@ the value that reset APIs reset to.
230
226
  ```ts
231
227
  readonly table = injectTable(() => ({
232
228
  features,
233
- rowModels: { /* … */ },
234
229
  columns,
235
230
  data: this.data(),
236
231
  initialState: {
@@ -276,9 +271,6 @@ export class Component {
276
271
 
277
272
  readonly table = injectTable(() => ({
278
273
  features,
279
- rowModels: {
280
- /* … */
281
- },
282
274
  columns,
283
275
  data: this.data(),
284
276
  state: {
@@ -335,9 +327,9 @@ right ownership model.** When you need more, see
335
327
  - **State type imports** — `PaginationState`, `SortingState`,
336
328
  `RowSelectionState`, `TableState<typeof features>`, etc.
337
329
  - **`createTableHook(...)`** — app-wide `injectAppTable` /
338
- `createAppColumnHelper` that pre-bind `features` and `rowModels`. Also
339
- exposes `tableComponents` / `cellComponents` / `headerComponents` registries
340
- (covered in `angular-rendering-directives`).
330
+ `createAppColumnHelper` that pre-bind `features` (which now carries row-model
331
+ factories and fn registries). Also exposes `tableComponents` / `cellComponents` /
332
+ `headerComponents` registries (covered in `angular-rendering-directives`).
341
333
 
342
334
  ---
343
335
 
@@ -359,15 +351,15 @@ import { injectTable, tableFeatures } from '@tanstack/angular-table'
359
351
  const features = tableFeatures({})
360
352
  const table = injectTable(() => ({
361
353
  features,
362
- rowModels: {},
363
354
  columns,
364
355
  data: data(),
365
356
  }))
366
357
  ```
367
358
 
368
359
  Also retired: `getFilteredRowModel`, `getSortedRowModel`, `getPaginationRowModel`
369
- as top-level options → migrated to `rowModels: { filteredRowModel: ..., sortedRowModel: ..., paginatedRowModel: ... }`
370
- with explicit `*Fns` parameters.
360
+ as top-level options → migrated to slots on the `features` object
361
+ (`filteredRowModel: createFilteredRowModel()`, `sortedRowModel: createSortedRowModel()`,
362
+ `paginatedRowModel: createPaginatedRowModel()`) with fn registries (`filterFns`, `sortFns`) also on `features`.
371
363
 
372
364
  ### 2. (CRITICAL) Missing API because feature not in `features`
373
365
 
@@ -394,9 +386,10 @@ Same for `setPageIndex`, `setPageSize`, `setSorting`, `toggleSorting`,
394
386
  ### 4. (HIGH) Expensive values declared **inside** the `injectTable` initializer
395
387
 
396
388
  Because the initializer re-runs when any reactive read inside it changes,
397
- declaring `columns`, `features`, `rowModels`, or feature-fn maps inside the
398
- function causes them to be recreated and re-applied on every data update.
399
- Move them outside the class or to stable class fields.
389
+ declaring `columns` or `features` inside the function causes them to be
390
+ recreated and re-applied on every data update (row-model factories and fn
391
+ registries are part of `features`, so moving `features` outside covers all of
392
+ them). Move them outside the class or to stable class fields.
400
393
 
401
394
  ### 5. (HIGH) Forgetting that the initializer re-runs
402
395
 
@@ -24,7 +24,6 @@ const paginationAtom = new Store<PaginationState>({ pageIndex: 0, pageSize: 10 }
24
24
 
25
25
  readonly table = injectTable(() => ({
26
26
  features,
27
- rowModels: { /* … */ },
28
27
  columns,
29
28
  data: this.data(),
30
29
  atoms: {
@@ -68,7 +67,7 @@ type MyTableState = TableState<typeof features>
68
67
 
69
68
  ## `createTableHook` — app-wide table infrastructure
70
69
 
71
- When multiple tables in an app share the same `features`, `rowModels`, and
70
+ When multiple tables in an app share the same `features` object and
72
71
  component conventions, factor them into a `createTableHook(...)` call once and
73
72
  import the resulting `injectAppTable` / `createAppColumnHelper`.
74
73
 
@@ -91,11 +90,13 @@ export const {
91
90
  injectTableCellContext,
92
91
  injectTableHeaderContext,
93
92
  } = createTableHook({
94
- features: tableFeatures({ rowSortingFeature, rowPaginationFeature }),
95
- rowModels: {
96
- sortedRowModel: createSortedRowModel(sortFns),
93
+ features: tableFeatures({
94
+ rowSortingFeature,
95
+ rowPaginationFeature,
96
+ sortedRowModel: createSortedRowModel(),
97
97
  paginatedRowModel: createPaginatedRowModel(),
98
- },
98
+ sortFns,
99
+ }),
99
100
  getRowId: (row) => row.id,
100
101
  })
101
102
  ```
@@ -110,7 +111,7 @@ const columnHelper = createAppColumnHelper<Person>() // only TData generic neede
110
111
  readonly table = injectAppTable(() => ({
111
112
  columns,
112
113
  data: this.data(),
113
- })) // features & rowModels inherited
114
+ })) // features (including row-model factories) inherited
114
115
  ```
115
116
 
116
117
  `createTableHook` also lets you register `tableComponents` / `cellComponents` /
@@ -23,7 +23,7 @@ export interface TanStackTableCellContext<
23
23
  * This token is provided by the {@link TanStackTableCell} directive.
24
24
  */
25
25
  export const TanStackTableCellToken = new InjectionToken<
26
- TanStackTableCellContext<any, any, any>['cell']
26
+ TanStackTableCellContext<TableFeatures, RowData, CellData>['cell']
27
27
  >('[TanStack Table] CellContext')
28
28
 
29
29
  /**
@@ -100,5 +100,9 @@ export function injectTableCellContext<
100
100
  TData extends RowData,
101
101
  TValue extends CellData,
102
102
  >(): TanStackTableCellContext<TFeatures, TData, TValue>['cell'] {
103
- return inject(TanStackTableCellToken)
103
+ return inject(TanStackTableCellToken) as unknown as TanStackTableCellContext<
104
+ TFeatures,
105
+ TData,
106
+ TValue
107
+ >['cell']
104
108
  }
@@ -333,10 +333,7 @@ export type CreateTableHookResult<
333
333
  TValue extends CellData,
334
334
  >() => CellContext<TFeatures, TData, TValue>
335
335
  injectAppTable: <TData extends RowData>(
336
- tableOptions: () => Omit<
337
- TableOptions<TFeatures, TData>,
338
- 'features' | 'rowModels'
339
- >,
336
+ tableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'>,
340
337
  ) => AppAngularTable<
341
338
  TFeatures,
342
339
  TData,
@@ -358,7 +355,6 @@ export type CreateTableHookResult<
358
355
  * ```ts
359
356
  * const { injectAppTable, createAppColumnHelper } = createTableHook({
360
357
  * features,
361
- * rowModels: {},
362
358
  * tableComponents: {},
363
359
  * cellComponents: {},
364
360
  * headerComponents: {},
@@ -424,10 +420,7 @@ export function createTableHook<
424
420
  TData extends RowData,
425
421
  TSelected = TableState<TFeatures>,
426
422
  >(
427
- tableOptions: () => Omit<
428
- TableOptions<TFeatures, TData>,
429
- 'features' | 'rowModels'
430
- >,
423
+ tableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'>,
431
424
  ): AppAngularTable<
432
425
  TFeatures,
433
426
  TData,
@@ -447,7 +440,7 @@ export function createTableHook<
447
440
  return footer as Header<TFeatures, TData, any> & THeaderComponents
448
441
  }
449
442
 
450
- const appTableFeatures: TableFeature<{}> = {
443
+ const appTableFeatures: TableFeature = {
451
444
  constructTableAPIs: (table) => {
452
445
  Object.assign(table, tableComponents, { appCell, appHeader, appFooter })
453
446
  },
@@ -467,8 +460,14 @@ export function createTableHook<
467
460
  ...defaultTableOptions.features,
468
461
  appTableFeatures,
469
462
  },
470
- } as TableOptions<TFeatures, TData>
471
- }) as AngularTable<any, any>
463
+ } as unknown as TableOptions<TFeatures, TData>
464
+ }) as unknown as AppAngularTable<
465
+ TFeatures,
466
+ TData,
467
+ TTableComponents,
468
+ TCellComponents,
469
+ THeaderComponents
470
+ >
472
471
  }
473
472
 
474
473
  function createAppColumnHelper<TData extends RowData>(): AppColumnHelper<