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.
Setup
import Component from '@glimmer/component'
import { tracked } from '@glimmer/tracking'
import {
createSortedRowModel,
createTableHook,
rowSortingFeature,
sortFn_alphanumeric,
tableFeatures,
} from '@tanstack/ember-table'
const features = tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns: { alphanumeric: sortFn_alphanumeric }, })
export const { appFeatures, createAppColumnHelper, createAppTable } = createTableHook({ features, enableSortingRemoval: false, })
type Person = { id: string; name: string } const columnHelper = createAppColumnHelper() const columns = columnHelper.columns([ columnHelper.accessor('name', { header: 'Name' }), ])
export default class PeopleTable extends Component { @tracked data: Person[] = []
table = createAppTable(() => ({ columns, data: this.data, })) }
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.
## Composition Contract
- Hook options supply stable shared `features` and default table options.
- `createAppColumnHelper<TData>()` binds the feature type while inferring each row type.
- `createAppTable(() => options)` merges shared defaults with table-specific options.
- Table-specific options win over shared defaults, except that the factory remains the feature owner.
- `appFeatures` exposes the exact registered feature set for helper types or reusable utilities.
Shared options intentionally exclude `columns`, `data`, and `state`. Keep model inputs and controlled state at each table call site; share conventions, not one mutable state object across unrelated tables.
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.
## Render Normally
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`.
## Common Mistakes
### HIGH Copying another adapter's component registry
Wrong: expect `table.AppTable`, `useTableContext`, or a `tableComponents` option from React, Solid, Svelte, or Lit examples.
Correct: use the normal Ember FlexRender components and pass the table through ordinary Ember composition when another component truly needs it.
The Ember hook shares features/defaults and type inference only.
### HIGH Passing features per table
Wrong:
```gts
createAppTable(() => ({ features: otherFeatures, columns, data: this.data }))
Correct: define a separate app hook when a table needs a genuinely different feature set. createAppTable omits features from its call-site type.
HIGH Recreating the hook or columns with tracked updates
Wrong: call createTableHook, createAppColumnHelper, or columns(...) inside a component getter or the createAppTable thunk.
Correct: create them once at module scope and let the table thunk read only changing inputs.
MEDIUM Sharing controlled state as a default
The hook options omit state. Put state and matching on[State]Change callbacks in each createAppTable thunk so every table has one clear owner per slice. Use a shared external atom only when tables intentionally coordinate the same compatible slice.
MEDIUM Repeating manual feature generics
Use createAppColumnHelper<Person>(); do not thread typeof features through every column helper after the factory has already captured it.
API Discovery
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.