@tanstack/lit-table
Version:
Headless UI for building powerful tables & datagrids for Lit.
156 lines (110 loc) • 6.46 kB
Markdown
---
name: table-state
description: >
Use TableController-selected table.state, table.atoms/store, stable table.subscribe selectors, controlled reactive properties plus on*Change, and external TanStack Store atoms. Load when Lit state appears stale, host updates are too broad, or table state must be shared outside the element.
metadata:
type: framework
library: '@tanstack/lit-table'
framework: lit
library_version: '9.2.4'
requires:
- '@tanstack/table-core#core'
- getting-started
sources:
- 'TanStack/table:docs/framework/lit/guide/table-state.md'
- 'TanStack/table:examples/lit/basic-external-state'
- 'TanStack/table:packages/lit-table/src/TableController.ts'
---
This skill builds on @tanstack/table-core#core and this package's getting-started skill.
## State Mental Model
TanStack Table is primarily a state coordinator. Keep state internal unless another subsystem needs to read, persist, or drive it. Without `initialState`, `atoms`, `state`, or `on[State]Change`, the table owns every registered slice.
- `table.baseAtoms` are internal writable atoms initialized from resolved initial state.
- `table.atoms` are readonly derived atoms for the active owner of each registered slice.
- `table.store` combines those atoms into one readonly flat store.
- `table.state` is only the value selected by the second `controller.table` argument.
`TableController` subscribes to state/options and requests host updates. State is feature-based: if pagination is missing from options, atoms, or types, register `rowPaginationFeature`. Keep `features`, `columns`, and `data` references stable across `render()` calls; never create arrays or derive rows inline in the `controller.table` options.
## Setup
```ts
import { LitElement, html } from 'lit'
import { customElement, state } from 'lit/decorators.js'
import {
TableController,
rowPaginationFeature,
tableFeatures,
} from '@tanstack/lit-table'
import type { PaginationState } from '@tanstack/lit-table'
type Item = { id: string }
const features = tableFeatures({ rowPaginationFeature })
const columns = [{ accessorKey: 'id' }]
const data: Item[] = [{ id: '1' }]
@customElement('paged-items')
export class PagedItems extends LitElement {
@state() private pagination: PaginationState = { pageIndex: 0, pageSize: 10 }
private controller = new TableController<typeof features, Item>(this)
protected render() {
const table = this.controller.table(
{
features,
columns,
data,
state: { pagination: this.pagination },
onPaginationChange: (updater) => {
this.pagination =
typeof updater === 'function' ? updater(this.pagination) : updater
},
},
(state) => ({ pagination: state.pagination }),
)
return html`<button @click=${() => table.nextPage()}>
Page ${table.state.pagination.pageIndex + 1}
</button>`
}
}
```
## Core Patterns
### Read selected render state from table.state
The second argument to `controller.table` defines the shape of `table.state`. Its selector should be stable when reused across renders.
### Read one current slice from its atom
`table.atoms.pagination.get()` returns the current pagination snapshot. Use it in event handlers; use selected state or `table.subscribe` when a template region must update reactively.
### Put narrow reactive islands in table.subscribe
```ts
const page = table.subscribe(
table.store,
(state) => state.pagination.pageIndex,
(pageIndex) => html`<span>Page ${pageIndex + 1}</span>`,
)
```
Keep the selector reference stable for repeated subscription sites.
## Choose State Ownership
Use exactly one owner per slice:
- Prefer internal state and feature APIs for table-local behavior.
- Use `initialState` for starting/reset values. Changing it later does not reset current state.
- Prefer a stable external TanStack Store atom in `atoms` when state is shared. Feature APIs write it directly, so omit `on[State]Change`.
- Use a reactive host property in `state.<slice>` plus its matching callback for simple controlled state. Write raw values and resolved updater functions back to the property.
External atoms take precedence over external `state`, which syncs into the internal base atom. Do not configure multiple owners. The global v8 `onStateChange` option is gone; subscribe to `table.store` if all state changes must be observed.
## Initialize, Update, and Reset
Prefer `setSorting`, `nextPage`, `toggleVisibility`, `toggleSelected`, and other feature methods. Direct `baseAtoms` writes are a rare escape hatch for internal state; write the supplied external atom when it owns the slice.
```ts
table.resetSorting()
table.resetPagination()
table.resetPagination(true)
```
Feature resets use `table.initialState` unless `true` requests the feature default and can update external owners. Core `table.reset()` resets internal base atoms only. Use slice types such as `PaginationState` and use `TableState<typeof features>` for the complete feature-inferred state.
## Common Mistakes
### HIGH Callback freezes controlled state
Wrong: provide `onPaginationChange` but omit `state.pagination`.
Correct: pass the current controlled value and write both direct values and updater functions back to the owning property.
An on-change callback marks the slice as externally managed; failing to feed the next value back leaves the table reading the old value.
Source: TanStack/table:docs/framework/lit/guide/table-state.md
### HIGH Snapshot read mistaken for subscription
Wrong: cache `const page = table.store.state.pagination.pageIndex` outside render and expect it to change.
Correct: read selected `table.state` during render or create a `table.subscribe` template island.
A store snapshot is current data, not a continuing Lit subscription.
Source: TanStack/table:packages/lit-table/src/TableController.ts
### MEDIUM Unstable selector defeats update gating
Wrong: create unrelated selector closures throughout the render tree.
Correct: define reusable selectors as stable class fields or module functions.
TableController shallow-compares selected output; stable selector intent avoids unnecessary subscription churn.
Source: TanStack/table:docs/framework/lit/guide/table-state.md
## API Discovery
Inspect `node_modules/@tanstack/lit-table/dist/TableController.d.ts`, then `node_modules/@tanstack/table-core/dist/core/table/` for state precedence and updater behavior.