CtrlK
BlogDocsLog inGet started
Tessl Logo

table-state

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.

70

Quality

87%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

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

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

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.

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.

Repository
TanStack/table
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.