@tanstack/ember-table
Version:
Headless UI for building powerful tables & datagrids for Ember.
118 lines (84 loc) • 5.42 kB
Markdown
---
name: create-table-hook
description: >
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.
metadata:
type: framework
library: '@tanstack/ember-table'
framework: ember
library_version: '9.0.0-beta.80'
requires:
- '@tanstack/table-core#core'
- getting-started
- table-state
sources:
- 'TanStack/table:docs/framework/ember/guide/composable-tables.md'
- 'TanStack/table:examples/ember/basic-app-table'
- 'TanStack/table:packages/ember-table/src/create-table-hook.ts'
---
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
```gts
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<Person>()
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.