@tanstack/ember-table 9.2.4 → 9.2.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ember-table",
3
- "version": "9.2.4",
3
+ "version": "9.2.6",
4
4
  "description": "Headless UI for building powerful tables & datagrids for Ember.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -57,40 +57,40 @@
57
57
  ],
58
58
  "dependencies": {
59
59
  "@embroider/addon-shim": "^1.10.3",
60
- "@tanstack/store": "^0.11.1",
61
- "@tanstack/table-core": "9.2.4",
60
+ "@tanstack/store": "^0.11.2",
61
+ "@tanstack/table-core": "9.2.6",
62
62
  "decorator-transforms": "2.4.0"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@babel/core": "7.29.7",
66
66
  "@babel/plugin-transform-typescript": "7.29.7",
67
- "@ember/app-tsconfig": "2.0.0",
68
- "@ember/library-tsconfig": "^2.0.0",
69
- "@ember/test-helpers": "^5.4.3",
70
- "@embroider/addon-dev": "^8.3.1",
71
- "@embroider/core": "4.6.3",
72
- "@embroider/macros": "1.20.6",
73
- "@embroider/vite": "^1.7.10",
67
+ "@ember/app-tsconfig": "2.1.0",
68
+ "@ember/library-tsconfig": "^3.0.0",
69
+ "@ember/test-helpers": "^5.7.0",
70
+ "@embroider/addon-dev": "^8.4.0",
71
+ "@embroider/core": "4.6.7",
72
+ "@embroider/macros": "1.21.1",
73
+ "@embroider/vite": "^1.7.13",
74
74
  "@eslint/js": "^10.0.1",
75
75
  "@glimmer/component": "2.1.1",
76
- "@glint/ember-tsc": "1.11.0",
77
- "@glint/template": "1.8.0",
78
- "@glint/tsserver-plugin": "2.7.3",
76
+ "@glint/ember-tsc": "1.11.6",
77
+ "@glint/template": "1.9.0",
78
+ "@glint/tsserver-plugin": "2.7.9",
79
79
  "@rollup/plugin-babel": "7.1.0",
80
80
  "@types/qunit": "^2.19.14",
81
81
  "babel-plugin-ember-template-compilation": "4.0.0",
82
- "ember-qunit": "^9.1.0",
83
- "ember-source": "7.2.0",
82
+ "ember-qunit": "^9.2.0",
83
+ "ember-source": "7.3.0",
84
84
  "ember-strict-application-resolver": "0.1.1",
85
85
  "eslint-config-prettier": "^10.1.8",
86
- "eslint-plugin-ember": "^13.5.0",
86
+ "eslint-plugin-ember": "^13.6.0",
87
87
  "eslint-plugin-import-x": "^4.17.1",
88
- "globals": "^17.11.0",
89
- "qunit": "^2.26.0",
88
+ "globals": "^17.13.0",
89
+ "qunit": "^3.0.0",
90
90
  "qunit-dom": "^3.6.0",
91
- "rollup": "^4.63.0",
92
- "testem": "^3.20.1",
93
- "typescript-eslint": "^8.68.0"
91
+ "rollup": "^4.64.0",
92
+ "testem": "^3.21.3",
93
+ "typescript-eslint": "^8.71.0"
94
94
  },
95
95
  "ember": {
96
96
  "edition": "octane"
@@ -1,36 +1,37 @@
1
1
  ---
2
2
  name: getting-started
3
- description: >
4
- Create a TanStack Ember Table v9 table with useTable, a tracked options thunk, stable tableFeatures and columns, .gts templates, FlexRenderCell/Header/Footer, and correctly bound template helpers. Load for first-table setup, Glimmer reactivity, component cell renderers, or adapting another framework's example to Ember.
3
+ description:
4
+ Create and render Table v9 with the ember adapter. Route reusable createTableHook
5
+ defaults, and framework setup; use table-state for reactive ownership.
5
6
  metadata:
6
7
  type: framework
7
8
  library: '@tanstack/ember-table'
8
9
  framework: ember
9
- library_version: '9.2.4'
10
+ library_version: '9.2.6'
10
11
  requires:
11
12
  - '@tanstack/table-core#core'
12
- - '@tanstack/table-core#table-features'
13
13
  sources:
14
- - 'TanStack/table:docs/framework/ember/quick-start.md'
15
- - 'TanStack/table:examples/ember/basic-table'
16
- - 'TanStack/table:packages/ember-table/src/index.ts'
17
- - 'TanStack/table:packages/ember-table/src/use-table.ts'
18
- - 'TanStack/table:packages/ember-table/src/FlexRender.gts'
14
+ - TanStack/table:docs/framework/ember/quick-start.md
15
+ - TanStack/table:examples/ember/basic-table
16
+ - TanStack/table:packages/ember-table/src/index.ts
17
+ - TanStack/table:packages/ember-table/src/use-table.ts
18
+ - TanStack/table:packages/ember-table/src/FlexRender.gts
19
+ - TanStack/table:docs/framework/ember/guide/composable-tables.md
20
+ - TanStack/table:examples/ember/basic-app-table
21
+ - TanStack/table:packages/ember-table/src/create-table-hook.ts
19
22
  ---
20
23
 
21
- This skill builds on `@tanstack/table-core#core` and `@tanstack/table-core#table-features`. Ember Table is headless: it supplies reactive table models and renderer helpers, while the application owns semantic markup, CSS, accessibility, and design-system components.
24
+ Load `intent load @tanstack/table-core#core` first for the headless model, stable inputs, and column inference.
22
25
 
23
- The v9 addon requires Ember 5.8 or newer with Embroider or ember-auto-import v2. Prefer `.gts`/`.gjs` template-tag components with Glint.
26
+ Ember 5.8 or newer requires Embroider or ember-auto-import v2. Prefer `.gts` or `.gjs` template-tag components with Glint.
24
27
 
25
28
  ## Setup
26
29
 
27
30
  ```gts
28
31
  import Component from '@glimmer/component'
29
32
  import { tracked } from '@glimmer/tracking'
30
- import { on } from '@ember/modifier'
31
33
  import {
32
34
  FlexRenderCell,
33
- FlexRenderHeader,
34
35
  createColumnHelper,
35
36
  tableFeatures,
36
37
  useTable,
@@ -55,41 +56,19 @@ const getAllCells = (
55
56
  export default class PeopleTable extends Component {
56
57
  @tracked data = initialData
57
58
 
58
- table = useTable(() => ({
59
+ table = useTable(this, () => ({
59
60
  features,
60
61
  columns,
61
62
  data: this.data,
62
63
  getRowId,
63
64
  }))
64
65
 
65
- get headerGroups() {
66
- return this.table.getHeaderGroups()
67
- }
68
-
69
66
  get rows() {
70
67
  return this.table.getRowModel().rows
71
68
  }
72
69
 
73
- addPerson = () => {
74
- this.data = [...this.data, { id: '2', name: 'Grace' }]
75
- }
76
-
77
70
  <template>
78
- <button type='button' {{on 'click' this.addPerson}}>Add person</button>
79
71
  <table>
80
- <thead>
81
- {{#each this.headerGroups as |group|}}
82
- <tr>
83
- {{#each group.headers as |header|}}
84
- <th colspan={{header.colSpan}}>
85
- {{#unless header.isPlaceholder}}
86
- <FlexRenderHeader @header={{header}} />
87
- {{/unless}}
88
- </th>
89
- {{/each}}
90
- </tr>
91
- {{/each}}
92
- </thead>
93
72
  <tbody>
94
73
  {{#each this.rows as |row|}}
95
74
  <tr>
@@ -104,57 +83,22 @@ export default class PeopleTable extends Component {
104
83
  }
105
84
  ```
106
85
 
107
- `useTable` takes an options thunk. Tracked properties read by that thunk update table options, and table API reads inside getters/templates participate in Glimmer tracking. Keep `features` and `columns` at stable module or component-lifetime scope; replace `@tracked data` only when data meaningfully changes.
108
-
109
- ## Ember-Specific Patterns
110
-
111
- ### Preserve method receivers in templates
112
-
113
- V9 table, column, row, cell, and header methods live on prototypes and require their receiver. Ember templates extract function references, so call methods in a getter or a small module-level helper:
114
-
115
- ```gts
116
- const getCanSort = (column: Column<typeof features, Person>) =>
117
- column.getCanSort()
118
-
119
- const toggleSort =
120
- (column: Column<typeof features, Person>) => (event: Event) =>
121
- column.getToggleSortingHandler()?.(event)
122
- ```
123
-
124
- Pass `toggleSort header.column` to `{{on}}`; do not pass an extracted Table method directly.
125
-
126
- ### Render definitions through the matching component
127
-
128
- Use `FlexRenderCell`, `FlexRenderHeader`, and `FlexRenderFooter` with their matching object. A definition may return a primitive or `flexRenderComponent(Component, options)`. The rendered component receives `@ctx` and optional `@options`; Table does not instantiate arbitrary component-library markup for you.
129
-
130
- ## Common Mistakes
131
-
132
- ### HIGH Passing an options object instead of a thunk
133
-
134
- Wrong: `useTable({ features, columns, data: this.data })`.
135
-
136
- Correct: `useTable(() => ({ features, columns, data: this.data }))`.
137
-
138
- The thunk is how tracked option reads are connected to the table.
139
-
140
- ### HIGH Passing an unbound prototype method to a template modifier
141
-
142
- Wrong: `{{on 'click' header.column.getToggleSortingHandler}}`.
143
-
144
- Correct: wrap the call in a helper that invokes the method on `header.column`, as shown above.
86
+ ## Construction and rendering
145
87
 
146
- An extracted v9 prototype method loses `this` and can throw or silently target the wrong receiver.
88
+ `useTable` takes an options thunk. Read tracked data inside it, and keep features, atoms, and columns stable. In a component, pass `this` as the owner to bind external-atom cleanup to the component lifecycle. The one-argument thunk form remains available for standalone tables.
147
89
 
148
- ### HIGH Recreating model inputs inside the options thunk
90
+ Read Table APIs in template-consumed getters. Column, row, cell, and header prototype methods require their receiver, so use a getter or module helper such as `getAllCells(row)` rather than an extracted method. Wrap event calls the same way.
149
91
 
150
- Wrong: `columns: columnHelper.columns(...)` or `data: this.data.slice()` inside `useTable(() => ...)`.
92
+ Use `FlexRenderCell`, `FlexRenderHeader`, and `FlexRenderFooter` for the matching object. Rendered components receive `@ctx` and optional `@options` through `flexRenderComponent(Component, options)`.
151
93
 
152
- Correct: keep columns stable and pass the tracked data reference directly. The thunk may rerun; it should not manufacture new model inputs on every tracked update.
94
+ With `tableFeatures({})`, render `row.getAllCells()`. Register `columnVisibilityFeature` before switching to `row.getVisibleCells()`.
153
95
 
154
- ### MEDIUM Using a visibility-aware API without its feature
96
+ ## Read for the task
155
97
 
156
- With `tableFeatures({})`, render `row.getAllCells()`. Add `columnVisibilityFeature` before using `row.getVisibleCells()`.
98
+ - When adding or configuring optional features, load `intent load @tanstack/table-core#table-features` and read only references for the requested behavior.
99
+ - For state ownership or reactive reads, read [table-state](../table-state/SKILL.md).
100
+ - When tables share features, defaults, or reusable UI, read [create-table-hook](references/create-table-hook.md).
157
101
 
158
- ## API Discovery
102
+ ## API discovery
159
103
 
160
- Inspect `node_modules/@tanstack/ember-table/declarations/index.d.ts` for exports, `use-table.d.ts` for options/reactivity behavior, `FlexRender.d.ts` and `flex-render.d.ts` for renderer contracts, and `node_modules/@tanstack/table-core/dist/features/<feature>/` for feature-gated APIs. Do not substitute React hooks, subscriptions, or component signatures.
104
+ Inspect `node_modules/@tanstack/ember-table/declarations/index.d.ts`, `use-table.d.ts`, `signal.d.ts`, and `FlexRender.d.ts`. Core feature APIs are in `node_modules/@tanstack/table-core/dist/features/`.
@@ -1,23 +1,8 @@
1
- ---
2
- name: create-table-hook
3
- description: >
4
- Share TanStack Ember Table v9 features, row-model slots, defaults, and inferred column helpers with createTableHook, createAppTable, createAppColumnHelper, and appFeatures. Load for recurring Ember table conventions, per-table overrides, or confusion with component/context registries from other adapters.
5
- metadata:
6
- type: framework
7
- library: '@tanstack/ember-table'
8
- framework: ember
9
- library_version: '9.2.4'
10
- requires:
11
- - '@tanstack/table-core#core'
12
- - getting-started
13
- - table-state
14
- sources:
15
- - 'TanStack/table:docs/framework/ember/guide/composable-tables.md'
16
- - 'TanStack/table:examples/ember/basic-app-table'
17
- - 'TanStack/table:packages/ember-table/src/create-table-hook.ts'
18
- ---
19
-
20
- This skill builds on `@tanstack/table-core#core`, `getting-started`, and `table-state`. Use an app factory when several tables share real features or defaults; keep a one-off table on `useTable`.
1
+ # Ember reusable table hooks
2
+
3
+ Read when multiple Ember tables share features, defaults, or rendering conventions. Keep standalone construction for tables without shared conventions. For controlled state or reactive reads, read [table-state](../../table-state/SKILL.md).
4
+
5
+ This reference inherits the version of its owning skill.
21
6
 
22
7
  ## Setup
23
8
 
@@ -53,16 +38,16 @@ const columns = columnHelper.columns([
53
38
  export default class PeopleTable extends Component {
54
39
  @tracked data: Person[] = []
55
40
 
56
- table = createAppTable(() => ({
41
+ table = createAppTable(this, () => ({
57
42
  columns,
58
43
  data: this.data,
59
44
  }))
60
45
  }
61
46
  ```
62
47
 
63
- Create the hook and column helpers at stable module scope. `createAppTable` preserves the `useTable` options thunk, so tracked reads such as `this.data` remain reactive. Do not pass `features` again at the table call site; the factory owns the feature set and binds it into column inference.
48
+ Create the hook and column helpers at stable module scope. Pass the component as the first `createAppTable` argument to bind external-atom cleanup to its lifecycle; the one-argument thunk form remains supported for standalone tables. `createAppTable` preserves the `useTable` options thunk, so tracked reads such as `this.data` remain reactive. Do not pass `features` again at the table call site; the factory owns the feature set and binds it into column inference.
64
49
 
65
- ## Composition Contract
50
+ ## Composition contract
66
51
 
67
52
  - Hook options supply stable shared `features` and default table options.
68
53
  - `createAppColumnHelper<TData>()` binds the feature type while inferring each row type.
@@ -74,11 +59,11 @@ Shared options intentionally exclude `columns`, `data`, and `state`. Keep model
74
59
 
75
60
  Sharing `rowSortingFeature`, `sortedRowModel`, and sorting defaults gives every table sorting capability; it does not make their internal sorting state shared. The factory technically accepts a stable external atom in shared `atoms`, but that deliberately couples tables and their column IDs. Prefer independent state unless synchronized tables are the actual product behavior.
76
61
 
77
- ## Render Normally
62
+ ## Render normally
78
63
 
79
- Ember's factory does not register `AppTable`, `AppCell`, `AppHeader`, context hooks, or reusable component registries. Continue rendering with `FlexRenderCell`, `FlexRenderHeader`, and `FlexRenderFooter`. Continue wrapping receiver-dependent v9 methods in Ember template helpers as described by `getting-started`.
64
+ Ember's factory does not register `AppTable`, `AppCell`, `AppHeader`, context hooks, or reusable component registries. Continue rendering with `FlexRenderCell`, `FlexRenderHeader`, and `FlexRenderFooter`. Continue wrapping receiver-dependent v9 methods in Ember template helpers as described by [getting-started](../SKILL.md).
80
65
 
81
- ## Common Mistakes
66
+ ## Common mistakes
82
67
 
83
68
  ### HIGH Copying another adapter's component registry
84
69
 
@@ -96,7 +81,7 @@ Wrong:
96
81
  createAppTable(() => ({ features: otherFeatures, columns, data: this.data }))
97
82
  ```
98
83
 
99
- Correct: define a separate app hook when a table needs a genuinely different feature set. `createAppTable` omits `features` from its call-site type.
84
+ Correct: define a separate app hook when a table needs a different feature set. `createAppTable` omits `features` from its call-site type.
100
85
 
101
86
  ### HIGH Recreating the hook or columns with tracked updates
102
87
 
@@ -112,6 +97,12 @@ The hook options omit `state`. Put `state` and matching `on[State]Change` callba
112
97
 
113
98
  Use `createAppColumnHelper<Person>()`; do not thread `typeof features` through every column helper after the factory has already captured it.
114
99
 
115
- ## API Discovery
100
+ ## API discovery
116
101
 
117
102
  Inspect `node_modules/@tanstack/ember-table/declarations/create-table-hook.d.ts` for the installed factory return shape, omitted options, and merge precedence. Inspect `use-table.d.ts` for thunk reactivity and `node_modules/@tanstack/table-core/dist/features/<feature>/` for the shared feature APIs. Do not infer Ember component/context behavior from another adapter's createTableHook.
103
+
104
+ ## Sources
105
+
106
+ - `TanStack/table:docs/framework/ember/guide/composable-tables.md`
107
+ - `TanStack/table:examples/ember/basic-app-table`
108
+ - `TanStack/table:packages/ember-table/src/create-table-hook.ts`
@@ -1,187 +1,83 @@
1
1
  ---
2
2
  name: table-state
3
- description: >
4
- Read, track, initialize, control, and reset TanStack Ember Table v9 state through Glimmer-reactive table APIs, baseAtoms, atoms, store, Ember createAtom, or @tracked state plus on*Change. Load for ownership precedence, updater callbacks, stale template state, options-thunk reactivity, or incorrect table.Subscribe/store.subscribe usage.
3
+ description:
4
+ Read and control Table v9 state in ember. Use for tracked reads, subscriptions,
5
+ controlled slices, and framework-specific reactive boundaries.
5
6
  metadata:
6
7
  type: framework
7
8
  library: '@tanstack/ember-table'
8
9
  framework: ember
9
- library_version: '9.2.4'
10
+ library_version: '9.2.6'
10
11
  requires:
11
- - '@tanstack/table-core#core'
12
- - getting-started
12
+ - '@tanstack/table-core#table-state'
13
13
  sources:
14
- - 'TanStack/table:docs/framework/ember/guide/table-state.md'
15
- - 'TanStack/table:examples/ember/basic-external-atoms'
16
- - 'TanStack/table:examples/ember/basic-external-state'
17
- - 'TanStack/table:packages/ember-table/src/use-table.ts'
18
- - 'TanStack/table:packages/ember-table/src/reactivity.ts'
19
- - 'TanStack/table:packages/ember-table/src/signal.ts'
14
+ - TanStack/table:docs/framework/ember/guide/table-state.md
15
+ - TanStack/table:examples/ember/basic-external-atoms
16
+ - TanStack/table:examples/ember/basic-external-state
17
+ - TanStack/table:packages/ember-table/src/use-table.ts
18
+ - TanStack/table:packages/ember-table/src/reactivity.ts
19
+ - TanStack/table:packages/ember-table/src/signal.ts
20
20
  ---
21
21
 
22
- This skill builds on `@tanstack/table-core#core` and `getting-started`. Keep state internal unless another part of the application must read, persist, validate, or drive a slice.
22
+ Load `intent load @tanstack/table-core#table-state` first for shared ownership, initialization, updater, and reset rules.
23
23
 
24
- ## State Surfaces
24
+ Glimmer tracks Table APIs, atom reads, and store-property reads inside template-consumed getters. A value cached outside tracked consumption is only a snapshot. There is no `table.Subscribe` or selector argument to `useTable`; `table.store.subscribe` is intentionally a no-op and cannot drive rendering.
25
25
 
26
- State exists only for registered features:
27
-
28
- - `table.baseAtoms` are the table's internal writable atoms.
29
- - `table.atoms` are readonly, ownership-aware atoms for each registered slice.
30
- - `table.store.state` exposes the current flat state.
31
- - Feature methods such as `setSorting`, `setPageSize`, and `row.toggleSelected()` are the preferred write path.
32
-
33
- Ember's reactivity feature bridges these reads into Glimmer tracking. There is no `table.Subscribe`, no selector argument to `useTable`, and no need to subscribe during rendering. Read the relevant API inside a template-consumed getter:
34
-
35
- ```gts
36
- get rows() {
37
- return this.table.getRowModel().rows
38
- }
39
-
40
- get pageIndex() {
41
- return this.table.atoms.pagination.get().pageIndex
42
- }
43
- ```
44
-
45
- `table.store.subscribe` is not the Ember rendering mechanism. The adapter's store is pull-based; Glimmer tracks individual property and atom reads.
46
-
47
- `features` and `atoms` are construct-time inputs in this adapter. Create them once before `useTable`; rerunning the options thunk updates ordinary live options but does not swap the table's feature set or atom owners.
48
-
49
- ## Choose One Owner Per Slice
50
-
51
- For a slice such as pagination, choose one source of truth:
52
-
53
- 1. Internal `baseAtoms` by default.
54
- 2. `initialState.pagination` for an internally owned starting value.
55
- 3. An external writable atom in `atoms.pagination`.
56
- 4. A `@tracked` value in `state.pagination`, paired with `onPaginationChange`.
57
-
58
- Read precedence is `atoms[key]` over `state[key]` over the internal `baseAtoms[key]`. Do not configure multiple owners merely as fallbacks.
59
-
60
- ## Internal and Initial State
61
-
62
- With no `initialState`, `atoms`, `state`, or `on[State]Change`, Table owns registered state internally. Use `initialState` when only the starting/reset value differs:
63
-
64
- ```gts
65
- table = useTable(() => ({
66
- features,
67
- columns,
68
- data: this.data,
69
- initialState: {
70
- pagination: { pageIndex: 0, pageSize: 25 },
71
- },
72
- }))
73
- ```
74
-
75
- Changing `initialState` later does not reset existing state. Prefer feature reset methods such as `resetPagination()`; passing `true` requests that feature's blank/default state. Core `table.reset()` resets internal base atoms and is not the primary reset path for externally owned atoms.
76
-
77
- ## External Ember Atoms
78
-
79
- Use the adapter's re-exported `createAtom` when a slice should be shared or read directly outside Table. It satisfies the TanStack Store atom contract and its reads are Glimmer-reactive:
26
+ ## Controlled tracked setup
80
27
 
81
28
  ```gts
29
+ import Component from '@glimmer/component'
30
+ import { tracked } from '@glimmer/tracking'
31
+ import { on } from '@ember/modifier'
82
32
  import {
83
- createAtom,
84
- useTable,
85
- type PaginationState,
86
- } from '@tanstack/ember-table'
87
-
88
- paginationAtom = createAtom<PaginationState>({
89
- pageIndex: 0,
90
- pageSize: 10,
91
- })
92
-
93
- table = useTable(() => ({
94
- features,
95
- columns,
96
- data: this.data,
97
- atoms: { pagination: this.paginationAtom },
98
- }))
99
-
100
- get pagination() {
101
- return this.paginationAtom.get()
102
- }
103
- ```
104
-
105
- Feature APIs write through the external atom, so do not add `onPaginationChange` for the same atom-owned slice. Write the external atom itself when using the low-level path.
106
-
107
- ## Controlled Tracked State
108
-
109
- Use `state` plus the matching callback when a `@tracked` property owns a slice. Resolve both raw values and updater functions:
110
-
111
- ```gts
112
- import {
113
- createPaginatedRowModel,
114
33
  rowPaginationFeature,
115
34
  tableFeatures,
35
+ useTable,
116
36
  type PaginationState,
117
37
  } from '@tanstack/ember-table'
118
38
 
119
- const features = tableFeatures({
120
- rowPaginationFeature,
121
- paginatedRowModel: createPaginatedRowModel(),
122
- })
123
-
124
- @tracked pagination: PaginationState = { pageIndex: 0, pageSize: 10 }
125
-
126
- table = useTable(() => ({
127
- features,
128
- columns,
129
- data: this.data,
130
- state: { pagination: this.pagination },
131
- onPaginationChange: (updater) => {
132
- this.pagination =
133
- typeof updater === 'function' ? updater(this.pagination) : updater
134
- },
135
- }))
136
- ```
137
-
138
- The pagination feature owns the state and APIs; the row-model slot enables client-side pagination. For server-owned pagination, keep `rowPaginationFeature`, omit the client row-model slot, and configure the matching manual/server data options. The options thunk must read `this.pagination`; assigning the tracked field reruns the thunk and feeds the controlled value back into Table. V9 has no global `onStateChange` option.
139
-
140
- ## Low-Level Writes and Types
141
-
142
- Use a feature method first. If internally owned state truly needs a low-level update:
143
-
144
- ```ts
145
- this.table.baseAtoms.pagination.set((old) => ({ ...old, pageIndex: 0 }))
146
- ```
147
-
148
- Do not write `table.atoms.pagination`; it is readonly and may point at an external owner. Infer the complete registered shape with `TableState<typeof features>` and use feature-specific types such as `PaginationState` or `SortingState` for owned fields.
149
-
150
- ## Common Mistakes
151
-
152
- ### CRITICAL Controlling a slice without writing callbacks back
153
-
154
- Wrong:
155
-
156
- ```gts
157
- state: { pagination: this.pagination },
158
- onPaginationChange: () => {},
39
+ const features = tableFeatures({ rowPaginationFeature })
40
+ const columns = [{ accessorKey: 'name' }]
41
+ const data = [{ name: 'Ada' }]
42
+
43
+ export default class PageStatus extends Component {
44
+ @tracked pagination: PaginationState = { pageIndex: 0, pageSize: 10 }
45
+ table = useTable(this, () => ({
46
+ features,
47
+ columns,
48
+ data,
49
+ state: { pagination: this.pagination },
50
+ onPaginationChange: (next) => {
51
+ this.pagination =
52
+ typeof next === 'function' ? next(this.pagination) : next
53
+ },
54
+ }))
55
+
56
+ get pageNumber() {
57
+ return this.table.atoms.pagination.get().pageIndex + 1
58
+ }
59
+
60
+ nextPage = () => this.table.nextPage()
61
+
62
+ <template>
63
+ <button type='button' {{on 'click' this.nextPage}}>Page
64
+ {{this.pageNumber}}</button>
65
+ </template>
66
+ }
159
67
  ```
160
68
 
161
- Correct: apply the value-or-updater to the tracked owner. Otherwise the slice is frozen at the controlled value.
162
-
163
- ### HIGH Expecting a snapshot read or subscription component
164
-
165
- Wrong: invent `<table.Subscribe>`, call `table.store.subscribe(...)` for rendering, or cache `table.store.state.pagination` outside a tracked getter.
166
-
167
- Correct: read `table.atoms.pagination.get()`, `table.store.state.pagination`, or the relevant Table API inside a template-consumed getter.
168
-
169
- `table.getState()` is removed v8 API, not an Ember state-read alternative.
170
-
171
- ### HIGH Mixing atom and state ownership
172
-
173
- If both `atoms.pagination` and `state.pagination` are present, the atom wins. Choose one owner so resets and writes have an unambiguous destination.
174
-
175
- ### HIGH Mutating controlled objects in place
176
-
177
- Wrong: `this.pagination.pageIndex++`.
69
+ ## Reactive boundaries
178
70
 
179
- Correct: assign a new tracked value or let `table.setPageIndex(...)` invoke the controlled updater. Glimmer and the options thunk need an observable owner update.
71
+ - Read tracked values inside the options thunk and assign callback results back to their tracked owner. Mutating `this.pagination.pageIndex++` does not notify the thunk.
72
+ - `features` and `atoms` are construction-time inputs. Updating ordinary live options does not replace the feature set or atom owners; create them once before `useTable`.
73
+ - Pass the component as the owner for lifecycle cleanup when external atoms are used. The standalone one-argument thunk form remains supported.
74
+ - Use the adapter's `createAtom` for external Glimmer-reactive ownership. Read it in a tracked getter and use feature APIs or the external atom to write.
75
+ - Keep features, columns, and data stable. Call receiver-dependent methods through getters or helpers rather than passing extracted methods to templates.
180
76
 
181
- ### MEDIUM Writing the internal base atom for an external owner
77
+ ## Read for the task
182
78
 
183
- When `atoms.pagination` owns the slice, changing `baseAtoms.pagination` does not replace the active value. Write the external atom or use the feature API.
79
+ For advanced reactive boundaries, shared atoms, or detailed subscription examples, read [reactivity](references/reactivity.md). For construction or rendering setup, read [getting-started](../getting-started/SKILL.md).
184
80
 
185
- ## API Discovery
81
+ ## API discovery
186
82
 
187
- Inspect `node_modules/@tanstack/ember-table/declarations/use-table.d.ts` for option/state precedence and the Glimmer bridge, `signal.d.ts` for `createAtom` behavior, and `reactivity.d.ts` for scheduling/tracking. Inspect the registered feature under `node_modules/@tanstack/table-core/dist/features/<feature>/` for its state type, update API, and reset semantics.
83
+ Inspect `node_modules/@tanstack/ember-table/declarations/index.d.ts`, `use-table.d.ts`, `signal.d.ts`, and `FlexRender.d.ts`. Core feature APIs are in `node_modules/@tanstack/table-core/dist/features/`.
@@ -0,0 +1,75 @@
1
+ # Ember Table reactivity
2
+
3
+ Read for Ember subscription boundaries, shared state, or updates that require more than the default rendering behavior.
4
+
5
+ This reference inherits the version of its owning skill.
6
+
7
+ ## Own shared state with an Ember atom
8
+
9
+ ```gts
10
+ import Component from '@glimmer/component'
11
+ import { on } from '@ember/modifier'
12
+ import {
13
+ createAtom,
14
+ rowPaginationFeature,
15
+ tableFeatures,
16
+ useTable,
17
+ type PaginationState,
18
+ } from '@tanstack/ember-table'
19
+
20
+ const features = tableFeatures({ rowPaginationFeature })
21
+ const columns = [{ accessorKey: 'name' }]
22
+ const data = [{ name: 'Ada' }]
23
+
24
+ export default class SharedPage extends Component {
25
+ paginationAtom = createAtom<PaginationState>({ pageIndex: 0, pageSize: 10 })
26
+ table = useTable(this, () => ({
27
+ features,
28
+ columns,
29
+ data,
30
+ atoms: { pagination: this.paginationAtom },
31
+ }))
32
+
33
+ get pageNumber() {
34
+ return this.paginationAtom.get().pageIndex + 1
35
+ }
36
+
37
+ nextPage = () => this.table.nextPage()
38
+
39
+ <template>
40
+ <button type='button' {{on 'click' this.nextPage}}>Page
41
+ {{this.pageNumber}}</button>
42
+ </template>
43
+ }
44
+ ```
45
+
46
+ The adapter re-exports `createAtom` with Glimmer-reactive reads. Feature APIs write through to that owner, so omit `onPaginationChange`. Supplying the component owner binds external-atom cleanup to its destruction. Use the one-argument thunk form only when no Ember owner is available.
47
+
48
+ ## Construction-time ownership
49
+
50
+ The options thunk updates ordinary live options from tracked reads, but features and external atom owners are fixed during construction. Keep them stable. Replacing an `atoms` object in a getter does not swap the table's owners.
51
+
52
+ ## Template tracking is pull-based
53
+
54
+ Read `table.atoms.<slice>.get()`, `table.store.state.<slice>`, or a Table API inside a template-consumed getter. There is no selector argument or `table.Subscribe`; `table.store.subscribe` is a no-op. Subscribe to a supplied external atom when a separate consumer actually needs push notifications.
55
+
56
+ `table.getState()` is removed v8 API. Caching a value outside tracked consumption does not establish Glimmer tracking.
57
+
58
+ ## Controlled state pitfalls
59
+
60
+ Resolve value-or-updater callbacks and assign a fresh tracked value. `this.pagination.pageIndex++` mutates an untracked nested property. A no-op callback freezes the controlled slice. Prefer `table.setPageIndex(...)` to invoke the callback with the feature's update rules.
61
+
62
+ An external atom wins over controlled state. Writing an internal base atom cannot replace the external value; use the feature API or write the external owner.
63
+
64
+ ## API discovery
65
+
66
+ Inspect `node_modules/@tanstack/ember-table/declarations/index.d.ts`, `use-table.d.ts`, `signal.d.ts`, and `FlexRender.d.ts`. Core feature APIs are in `node_modules/@tanstack/table-core/dist/features/`.
67
+
68
+ ## Sources
69
+
70
+ - `TanStack/table:docs/framework/ember/guide/table-state.md`
71
+ - `TanStack/table:examples/ember/basic-external-atoms`
72
+ - `TanStack/table:examples/ember/basic-external-state`
73
+ - `TanStack/table:packages/ember-table/src/use-table.ts`
74
+ - `TanStack/table:packages/ember-table/src/reactivity.ts`
75
+ - `TanStack/table:packages/ember-table/src/signal.ts`