SuperAPI AI Directory

tanstack-ember-table-table-state

Frontend & websites

View repository

Updated 10-16 00:02

About this skill

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.

State Surfaces

State exists only for registered features:

  • table.baseAtoms are the table's internal writable atoms.
  • table.atoms are readonly, ownership-aware atoms for each registered slice.
  • table.store.state exposes the current flat state.
  • Feature methods such as setSorting, setPageSize, and row.toggleSelected() are the preferred write path.

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:

get rows() {
  return this.table.getRowModel().rows
}

get pageIndex() { return this.table.atoms.pagination.get().pageIndex }


`table.store.subscribe` is not the Ember rendering mechanism. The adapter's store is pull-based; Glimmer tracks individual property and atom reads.

`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.

## Choose One Owner Per Slice

For a slice such as pagination, choose one source of truth:

1. Internal `baseAtoms` by default.
2. `initialState.pagination` for an internally owned starting value.
3. An external writable atom in `atoms.pagination`.
4. A `@tracked` value in `state.pagination`, paired with `onPaginationChange`.

Read precedence is `atoms[key]` over `state[key]` over the internal `baseAtoms[key]`. Do not configure multiple owners merely as fallbacks.

## Internal and Initial State

With no `initialState`, `atoms`, `state`, or `on[State]Change`, Table owns registered state internally. Use `initialState` when only the starting/reset value differs:

```gts
table = useTable(() => ({
  features,
  columns,
  data: this.data,
  initialState: {
    pagination: { pageIndex: 0, pageSize: 25 },
  },
}))

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.

External Ember Atoms

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:

import {
  createAtom,
  useTable,
  type PaginationState,
} from '@tanstack/ember-table'

paginationAtom = createAtom<PaginationState>({
  pageIndex: 0,
  pageSize: 10,
})

table = useTable(() => ({
  features,
  columns,
  data: this.data,
  atoms: { pagination: this.paginationAtom },
}))

get pagination() {
  return this.paginationAtom.get()
}

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.

Controlled Tracked State

Use state plus the matching callback when a @tracked property owns a slice. Resolve both raw values and updater functions:

import {
  createPaginatedRowModel,
  rowPaginationFeature,
  tableFeatures,
  type PaginationState,
} from '@tanstack/ember-table'

const features = tableFeatures({
  rowPaginationFeature,
  paginatedRowModel: createPaginatedRowModel(),
})

@tracked pagination: PaginationState = { pageIndex: 0, pageSize: 10 }

table = useTable(() => ({
  features,
  columns,
  data: this.data,
  state: { pagination: this.pagination },
  onPaginationChange: (updater) => {
    this.pagination =
      typeof updater === 'function' ? updater(this.pagination) : updater
  },
}))

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.

Low-Level Writes and Types

Use a feature method first. If internally owned state truly needs a low-level update:

this.table.baseAtoms.pagination.set((old) => ({ ...old, pageIndex: 0 }))

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.

Common Mistakes

CRITICAL Controlling a slice without writing callbacks back

Wrong:

state: { pagination: this.pagination },
onPaginationChange: () => {},

Correct: apply the value-or-updater to the tracked owner. Otherwise the slice is frozen at the controlled value.

HIGH Expecting a snapshot read or subscription component

Wrong: invent <table.Subscribe>, call table.store.subscribe(...) for rendering, or cache table.store.state.pagination outside a tracked getter.

Correct: read table.atoms.pagination.get(), table.store.state.pagination, or the relevant Table API inside a template-consumed getter.

table.getState() is removed v8 API, not an Ember state-read alternative.

HIGH Mixing atom and state ownership

If both atoms.pagination and state.pagination are present, the atom wins. Choose one owner so resets and writes have an unambiguous destination.

HIGH Mutating controlled objects in place

Wrong: this.pagination.pageIndex++.

Correct: assign a new tracked value or let table.setPageIndex(...) invoke the controlled updater. Glimmer and the options thunk need an observable owner update.

MEDIUM Writing the internal base atom for an external owner

When atoms.pagination owns the slice, changing baseAtoms.pagination does not replace the active value. Write the external atom or use the feature API.

API Discovery

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.