Skip to content

Commit a7aae8b

Browse files
committed
docs: helpers guide, fix some example tsc errors
1 parent a471632 commit a7aae8b

17 files changed

Lines changed: 297 additions & 105 deletions

File tree

‎docs/config.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,16 @@
7777
"label": "Core Guides",
7878
"children": [
7979
{ "label": "Data", "to": "guide/data" },
80-
{ "label": "Column Defs", "to": "guide/column-defs" },
80+
{ "label": "Column Definitions", "to": "guide/column-definitions" },
8181
{ "label": "Table Instance", "to": "guide/tables" },
8282
{ "label": "Row Models", "to": "guide/row-models" },
8383
{ "label": "Rows", "to": "guide/rows" },
8484
{ "label": "Cells", "to": "guide/cells" },
8585
{ "label": "Header Groups", "to": "guide/header-groups" },
8686
{ "label": "Headers", "to": "guide/headers" },
8787
{ "label": "Columns", "to": "guide/columns" },
88-
{ "label": "Table and Column Meta", "to": "guide/table-and-column-meta" }
88+
{ "label": "Table and Column Meta", "to": "guide/table-and-column-meta" },
89+
{ "label": "Type Helpers", "to": "guide/helpers" }
8990
],
9091
"frameworks": [
9192
{

‎docs/guide/helpers.md‎

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
---
2+
title: Helpers Guide
3+
---
4+
5+
## Helpers Guide
6+
7+
TanStack Table exposes a few small helper functions for authoring table configuration with better TypeScript inference. They do not add behavior by themselves. Instead, they help TypeScript preserve the relationship between your feature set, row type, column definitions, table options, and named function registries.
8+
9+
The most common helpers are:
10+
11+
- `tableFeatures` - defines the static feature set and feature-related slots for a table
12+
- `tableOptions` - preserves inference when composing reusable table options
13+
- `createColumnHelper` - creates strongly typed column definitions
14+
15+
These helpers are exported from every framework adapter and from `@tanstack/table-core`.
16+
17+
## `tableFeatures`
18+
19+
Use `tableFeatures()` to declare which features a table uses. Features are opt-in in v9, so the object you pass to this helper determines which feature APIs and state slices exist on the table, columns, rows, headers, and cells.
20+
21+
For a basic table that only needs core behavior, pass an empty object:
22+
23+
```ts
24+
import { tableFeatures } from '@tanstack/react-table'
25+
26+
const features = tableFeatures({})
27+
```
28+
29+
Add feature objects and row model factories when your table needs extra behavior:
30+
31+
```ts
32+
import {
33+
columnFilteringFeature,
34+
createFilteredRowModel,
35+
createSortedRowModel,
36+
filterFns,
37+
rowSortingFeature,
38+
sortFns,
39+
tableFeatures,
40+
} from '@tanstack/react-table'
41+
42+
const features = tableFeatures({
43+
columnFilteringFeature,
44+
rowSortingFeature,
45+
filteredRowModel: createFilteredRowModel(),
46+
sortedRowModel: createSortedRowModel(),
47+
filterFns,
48+
sortFns,
49+
})
50+
```
51+
52+
Keep `features` stable. In most apps, define it outside your component or in shared table setup code. Since `typeof features` is used throughout the table's types, stable features also make it easier to reuse column helpers, column definitions, and shared options.
53+
54+
### What goes in `tableFeatures`
55+
56+
`tableFeatures()` is the static configuration point for things that are stitched into a table before it is created:
57+
58+
- Feature objects such as `rowSortingFeature`, `columnFilteringFeature`, or custom features
59+
- Row model factories such as `createSortedRowModel()` and `createFilteredRowModel()`
60+
- Function registries such as `filterFns`, `sortFns`, and `aggregationFns`
61+
- Type-only slots such as `tableMeta` and `columnMeta`, usually declared with `metaHelper`
62+
63+
`metaHelper()` is useful when declaring type-only meta slots in `tableFeatures()`. It is equivalent to writing `{} as MyMeta`, but makes the intent clearer and avoids lint rules accidentally removing the type assertion:
64+
65+
```ts
66+
import { metaHelper, rowSortingFeature, tableFeatures } from '@tanstack/react-table'
67+
68+
type MyTableMeta = {
69+
updateData: (rowIndex: number, columnId: string, value: unknown) => void
70+
}
71+
72+
type MyColumnMeta = {
73+
align?: 'left' | 'right'
74+
}
75+
76+
const features = tableFeatures({
77+
rowSortingFeature,
78+
tableMeta: metaHelper<MyTableMeta>(),
79+
columnMeta: metaHelper<MyColumnMeta>(),
80+
})
81+
```
82+
83+
For more detail on `tableMeta`, `columnMeta`, and `metaHelper`, see the [Table and Column Meta Guide](./table-and-column-meta).
84+
85+
Function registry keys become valid string references in table and column options:
86+
87+
```ts
88+
import {
89+
createColumnHelper,
90+
createSortedRowModel,
91+
rowSortingFeature,
92+
sortFns,
93+
tableFeatures,
94+
} from '@tanstack/react-table'
95+
96+
type Person = {
97+
rank: number
98+
}
99+
100+
const features = tableFeatures({
101+
rowSortingFeature,
102+
sortedRowModel: createSortedRowModel(),
103+
sortFns: {
104+
...sortFns,
105+
byRank: (rowA, rowB, columnId) => {
106+
return rowA.getValue<number>(columnId) - rowB.getValue<number>(columnId)
107+
},
108+
},
109+
})
110+
111+
const columnHelper = createColumnHelper<typeof features, Person>()
112+
113+
const columns = columnHelper.columns([
114+
columnHelper.accessor('rank', {
115+
sortFn: 'byRank',
116+
}),
117+
])
118+
```
119+
120+
For more detail on row model slots and function registries, see the [Row Models Guide](./row-models).
121+
122+
## `tableOptions`
123+
124+
Use `tableOptions()` when you want to compose reusable table option objects while keeping TanStack Table's generic inference intact. The helper returns the same object at runtime; its value is in the TypeScript overloads.
125+
126+
This is useful for shared defaults:
127+
128+
```ts
129+
import { tableFeatures, tableOptions, useTable } from '@tanstack/react-table'
130+
131+
const features = tableFeatures({})
132+
133+
const sharedOptions = tableOptions({
134+
features,
135+
defaultColumn: {
136+
minSize: 80,
137+
maxSize: 400,
138+
},
139+
debugTable: true,
140+
})
141+
142+
const table = useTable({
143+
...sharedOptions,
144+
columns,
145+
data,
146+
})
147+
```
148+
149+
It is also useful when a wrapper or table factory supplies required options later. For example, a reusable option fragment can omit `data`, `columns`, or `features` and still keep the remaining table option types connected to the same feature set and row type:
150+
151+
```ts
152+
import {
153+
createSortedRowModel,
154+
rowSortingFeature,
155+
tableFeatures,
156+
tableOptions,
157+
useTable,
158+
} from '@tanstack/react-table'
159+
160+
type Person = {
161+
firstName: string
162+
lastName: string
163+
age: number
164+
}
165+
166+
const features = tableFeatures({
167+
rowSortingFeature,
168+
sortedRowModel: createSortedRowModel(),
169+
})
170+
171+
const sortableDefaults = tableOptions<typeof features, Person>({
172+
initialState: {
173+
sorting: [{ id: 'lastName', desc: false }],
174+
},
175+
})
176+
177+
const table = useTable({
178+
features,
179+
columns,
180+
data,
181+
...sortableDefaults,
182+
})
183+
```
184+
185+
Use `tableOptions()` for reusable configuration. For one-off table setup, passing the object directly to your adapter's table creation function is usually enough.
186+
187+
## `createColumnHelper`
188+
189+
`createColumnHelper()` creates a small set of utilities for authoring column definitions:
190+
191+
- `accessor` creates data columns from an accessor key or accessor function
192+
- `display` creates display-only columns such as actions or selection controls
193+
- `group` creates parent columns that contain nested columns
194+
- `columns` wraps arrays of column definitions to preserve each column's individual value type
195+
196+
The helper is typed by both your feature set and row type:
197+
198+
```ts
199+
import { createColumnHelper, tableFeatures } from '@tanstack/react-table'
200+
201+
type Person = {
202+
firstName: string
203+
lastName: string
204+
age: number
205+
}
206+
207+
const features = tableFeatures({})
208+
const columnHelper = createColumnHelper<typeof features, Person>()
209+
210+
const columns = columnHelper.columns([
211+
columnHelper.accessor('firstName', {
212+
header: 'First Name',
213+
}),
214+
columnHelper.accessor((row) => row.lastName, {
215+
id: 'lastName',
216+
header: 'Last Name',
217+
}),
218+
columnHelper.display({
219+
id: 'actions',
220+
cell: (info) => `Actions for ${info.row.id}`,
221+
}),
222+
])
223+
```
224+
225+
At runtime, the column helper only returns column definition objects. Its main purpose is to infer accessor values, feature-specific column options, and nested column arrays more precisely than plain object literals can in some TypeScript scenarios.
226+
227+
Column definitions have their own guide with the full details on accessor columns, display columns, grouping columns, IDs, and rendering. See the [Column Defs Guide](./column-defs).
228+
229+
## When to Use Each Helper
230+
231+
Use `tableFeatures()` for every v9 table. Even an empty `tableFeatures({})` call clearly declares that the table only uses core features.
232+
233+
Use `tableOptions()` when options are shared, composed, or supplied in multiple steps. Skip it when the options object is only passed directly to `useTable`, `createTable`, `injectTable`, `TableController`, or `constructTable`.
234+
235+
Use `createColumnHelper()` when you want strong column inference, especially for accessor values, custom feature options, and nested column groups. Plain `ColumnDef` objects are still valid when they are easier for your use case.

‎docs/guide/table-and-column-meta.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ There are two kinds of meta:
1111
- **Table meta** - The `meta` table option. Pass any object and read it back anywhere via `table.options.meta`. A classic use case is passing an `updateData` function down to editable cells.
1212
- **Column meta** - The `meta` property on a column definition. Read it back anywhere a column is available via `column.columnDef.meta`. A classic use case is declaring which filter UI variant a column's header should render.
1313

14+
### Table Meta
15+
1416
<!-- ::start:framework -->
1517

1618
# React

‎docs/overview.md‎

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -216,24 +216,6 @@ TanStack Table will help you build just about any type of table you can imagine.
216216
- [Row Selection](./framework/lit/guide/row-selection) - Select/deselect rows (checkboxes)
217217
- [Row Sorting](./framework/lit/guide/sorting) - Sort rows by column values
218218

219-
# Vanilla
220-
221-
- Column Faceting - List unique lists of column values or min/max values for a column
222-
- Column Filtering - Filter rows based on search values for a column
223-
- Column Grouping - Group columns together, run aggregations, and more
224-
- Column Ordering - Dynamically change the order of columns
225-
- Column Pinning - Pin (Freeze) columns to the left or right of the table
226-
- Column Resizing - Let users resize columns with drag handles
227-
- Column Sizing - Dynamically change the size of columns
228-
- Column Visibility - Hide/show columns
229-
- Global Faceting - List unique lists of column values or min/max values for the entire table
230-
- Global Filtering - Filter rows based on search values for the entire table
231-
- Row Expanding - Expand/collapse rows (sub-rows)
232-
- [Row Pagination](./framework/vanilla/guide/pagination) - Paginate rows
233-
- Row Pinning - Pin (Freeze) rows to the top or bottom of the table
234-
- Row Selection - Select/deselect rows (checkboxes)
235-
- [Row Sorting](./framework/vanilla/guide/sorting) - Sort rows by column values
236-
237219
<!-- ::end:framework -->
238220

239221
These are just some of the capabilities that you can build with TanStack Table. There are many more features that are possible with TanStack Table that you can add along-side the built-in features.

‎examples/angular/composable-tables/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"start": "ng serve --port 5173",
66
"dev": "ng serve --port 5173",
77
"build": "ng build",
8+
"test:types": "ng build",
89
"lint": "eslint ./src",
910
"watch": "ng build --watch --configuration development"
1011
},

‎examples/lit/composable-tables/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"build": "vite build",
77
"serve": "vite preview",
88
"start": "vite",
9+
"test:types": "tsc --noEmit",
910
"lint": "eslint ./src"
1011
},
1112
"dependencies": {

‎examples/lit/composable-tables/src/components/products-table.ts‎

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,14 @@ export class ProductsTable extends LitElement {
6767
// NOTE: Capture `this` as `host` to avoid infinite recursion in the getter
6868
private appTable = (() => {
6969
const host = this
70-
return useAppTable(
71-
this,
72-
{
73-
debugTable: true,
74-
columns,
75-
get data() {
76-
return host.data
77-
},
78-
enableRowSelection: true,
70+
return useAppTable(this, {
71+
debugTable: true,
72+
columns,
73+
get data() {
74+
return host.data
7975
},
80-
(state) => ({
81-
pagination: state.pagination,
82-
sorting: state.sorting,
83-
columnFilters: state.columnFilters,
84-
rowSelection: state.rowSelection,
85-
}),
86-
)
76+
enableRowSelection: true,
77+
})
8778
})()
8879

8980
private refreshData = () => {

‎examples/lit/composable-tables/src/components/table-components.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ export class PaginationControls extends LitElement {
4848
<span>
4949
Page
5050
<strong>
51-
${(table.getState().pagination.pageIndex + 1).toLocaleString()} of
51+
${(table.state.pagination.pageIndex + 1).toLocaleString()} of
5252
${table.getPageCount().toLocaleString()}
5353
</strong>
5454
</span>
5555
<span>
5656
| Go to page:
5757
<input
5858
type="number"
59-
.value=${String(table.getState().pagination.pageIndex + 1)}
59+
.value=${String(table.state.pagination.pageIndex + 1)}
6060
@change=${(e: Event) => {
6161
const page = (e.target as HTMLInputElement).value
6262
? Number((e.target as HTMLInputElement).value) - 1
@@ -67,7 +67,7 @@ export class PaginationControls extends LitElement {
6767
/>
6868
</span>
6969
<select
70-
.value=${String(table.getState().pagination.pageSize)}
70+
.value=${String(table.state.pagination.pageSize)}
7171
@change=${(e: Event) => {
7272
table.setPageSize(Number((e.target as HTMLSelectElement).value))
7373
}}

‎examples/lit/composable-tables/src/components/users-table.ts‎

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,14 @@ export class UsersTable extends LitElement {
8181
// (causing infinite recursion if we wrote `get data() { return this.data }`).
8282
private appTable = (() => {
8383
const host = this
84-
return useAppTable(
85-
this,
86-
{
87-
columns,
88-
get data() {
89-
return host.data
90-
},
91-
enableRowSelection: true,
92-
debugTable: true,
84+
return useAppTable(this, {
85+
columns,
86+
get data() {
87+
return host.data
9388
},
94-
(state) => ({
95-
pagination: state.pagination,
96-
sorting: state.sorting,
97-
columnFilters: state.columnFilters,
98-
rowSelection: state.rowSelection,
99-
}),
100-
)
89+
enableRowSelection: true,
90+
debugTable: true,
91+
})
10192
})()
10293

10394
private refreshData = () => {

0 commit comments

Comments
 (0)