@tanstack/table-core 9.0.0-beta.37 → 9.0.0-beta.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/core/headers/buildHeaderGroups.cjs.map +1 -1
- package/dist/core/headers/buildHeaderGroups.d.cts +1 -1
- package/dist/core/headers/buildHeaderGroups.d.ts +1 -1
- package/dist/core/headers/buildHeaderGroups.js.map +1 -1
- package/dist/core/headers/coreHeadersFeature.utils.cjs +7 -7
- package/dist/core/headers/coreHeadersFeature.utils.cjs.map +1 -1
- package/dist/core/headers/coreHeadersFeature.utils.js +7 -7
- package/dist/core/headers/coreHeadersFeature.utils.js.map +1 -1
- package/dist/core/table/coreTablesFeature.utils.cjs +1 -1
- package/dist/core/table/coreTablesFeature.utils.cjs.map +1 -1
- package/dist/core/table/coreTablesFeature.utils.js +1 -1
- package/dist/core/table/coreTablesFeature.utils.js.map +1 -1
- package/dist/features/column-ordering/columnOrderingFeature.types.d.cts +5 -5
- package/dist/features/column-ordering/columnOrderingFeature.types.d.ts +5 -5
- package/dist/features/column-ordering/columnOrderingFeature.utils.cjs +6 -6
- package/dist/features/column-ordering/columnOrderingFeature.utils.cjs.map +1 -1
- package/dist/features/column-ordering/columnOrderingFeature.utils.d.cts +3 -3
- package/dist/features/column-ordering/columnOrderingFeature.utils.d.ts +3 -3
- package/dist/features/column-ordering/columnOrderingFeature.utils.js +6 -6
- package/dist/features/column-ordering/columnOrderingFeature.utils.js.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.cjs +44 -39
- package/dist/features/column-pinning/columnPinningFeature.cjs.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.d.cts +6 -1
- package/dist/features/column-pinning/columnPinningFeature.d.ts +6 -1
- package/dist/features/column-pinning/columnPinningFeature.js +45 -40
- package/dist/features/column-pinning/columnPinningFeature.js.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.types.d.cts +49 -38
- package/dist/features/column-pinning/columnPinningFeature.types.d.ts +49 -38
- package/dist/features/column-pinning/columnPinningFeature.utils.cjs +154 -146
- package/dist/features/column-pinning/columnPinningFeature.utils.cjs.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.utils.d.cts +81 -73
- package/dist/features/column-pinning/columnPinningFeature.utils.d.ts +81 -73
- package/dist/features/column-pinning/columnPinningFeature.utils.js +141 -133
- package/dist/features/column-pinning/columnPinningFeature.utils.js.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.cjs +4 -4
- package/dist/features/column-sizing/columnSizingFeature.cjs.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.js +5 -5
- package/dist/features/column-sizing/columnSizingFeature.js.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.types.d.cts +18 -12
- package/dist/features/column-sizing/columnSizingFeature.types.d.ts +18 -12
- package/dist/features/column-sizing/columnSizingFeature.utils.cjs +24 -20
- package/dist/features/column-sizing/columnSizingFeature.utils.cjs.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.utils.d.cts +18 -14
- package/dist/features/column-sizing/columnSizingFeature.utils.d.ts +18 -14
- package/dist/features/column-sizing/columnSizingFeature.utils.js +24 -20
- package/dist/features/column-sizing/columnSizingFeature.utils.js.map +1 -1
- package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs +16 -16
- package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs.map +1 -1
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.cts +3 -3
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.ts +3 -3
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js +16 -16
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js.map +1 -1
- package/dist/static-functions.cjs +16 -16
- package/dist/static-functions.d.cts +3 -3
- package/dist/static-functions.d.ts +3 -3
- package/dist/static-functions.js +3 -3
- package/package.json +1 -1
- package/skills/api-not-found/SKILL.md +113 -0
- package/skills/client-vs-server/SKILL.md +164 -0
- package/skills/column-faceting/SKILL.md +91 -0
- package/skills/column-filtering/SKILL.md +82 -0
- package/skills/column-ordering/SKILL.md +75 -0
- package/skills/column-pinning/SKILL.md +89 -0
- package/skills/column-resizing/SKILL.md +91 -0
- package/skills/column-sizing/SKILL.md +72 -0
- package/skills/column-visibility/SKILL.md +75 -0
- package/skills/core/SKILL.md +140 -0
- package/skills/custom-features/SKILL.md +207 -0
- package/skills/expanding/SKILL.md +80 -0
- package/skills/global-filtering/SKILL.md +84 -0
- package/skills/grouping/SKILL.md +50 -394
- package/skills/migrate-v8-to-v9/SKILL.md +230 -390
- package/skills/pagination/SKILL.md +35 -344
- package/skills/row-pinning/SKILL.md +47 -238
- package/skills/row-selection/SKILL.md +39 -351
- package/skills/sorting/SKILL.md +35 -299
- package/skills/table-features/SKILL.md +153 -0
- package/skills/typescript/SKILL.md +126 -0
- package/src/core/headers/buildHeaderGroups.ts +1 -1
- package/src/core/headers/coreHeadersFeature.utils.ts +7 -7
- package/src/core/table/coreTablesFeature.utils.ts +1 -1
- package/src/features/column-ordering/columnOrderingFeature.types.ts +5 -5
- package/src/features/column-ordering/columnOrderingFeature.utils.ts +9 -9
- package/src/features/column-pinning/columnPinningFeature.ts +64 -59
- package/src/features/column-pinning/columnPinningFeature.types.ts +49 -38
- package/src/features/column-pinning/columnPinningFeature.utils.ts +163 -155
- package/src/features/column-sizing/columnSizingFeature.ts +6 -6
- package/src/features/column-sizing/columnSizingFeature.types.ts +18 -12
- package/src/features/column-sizing/columnSizingFeature.utils.ts +31 -27
- package/src/features/column-visibility/columnVisibilityFeature.utils.ts +15 -15
- package/skills/column-definitions/SKILL.md +0 -330
- package/skills/column-layout/SKILL.md +0 -326
- package/skills/column-layout/references/subsystems.md +0 -220
- package/skills/customizing-feature-behavior/SKILL.md +0 -423
- package/skills/filtering/SKILL.md +0 -375
- package/skills/filtering/references/faceting-and-fuzzy.md +0 -218
- package/skills/row-expanding/SKILL.md +0 -356
- package/skills/setup/SKILL.md +0 -390
- package/skills/state-management/SKILL.md +0 -403
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: customizing-feature-behavior
|
|
3
|
-
description: >
|
|
4
|
-
Override per-column `sortFn`, `filterFn`, `aggregationFn` and table-level
|
|
5
|
-
`globalFilterFn` in TanStack Table v9. Covers built-in `filterFns` / `sortFns` /
|
|
6
|
-
`aggregationFns` registries (registered as slots on `tableFeatures({...})`),
|
|
7
|
-
authoring custom functions with the `FilterFn` / `SortFn` / `AggregationFn`
|
|
8
|
-
signatures, chaining filter→sort via the `addMeta` callback +
|
|
9
|
-
`row.columnFiltersMeta`, `resolveFilterValue`, `autoRemove`, `invertSorting`,
|
|
10
|
-
`sortUndefined` ('first'|'last'|-1|1), and `sortDescFirst`. Distinguishes
|
|
11
|
-
`aggregationFn` (produces value) from `aggregatedCell` (renders value).
|
|
12
|
-
type: core
|
|
13
|
-
library: tanstack-table
|
|
14
|
-
library_version: '9.0.0-alpha.48'
|
|
15
|
-
requires:
|
|
16
|
-
- state-management
|
|
17
|
-
sources:
|
|
18
|
-
- TanStack/table:docs/guide/sorting.md
|
|
19
|
-
- TanStack/table:docs/guide/column-filtering.md
|
|
20
|
-
- TanStack/table:docs/guide/fuzzy-filtering.md
|
|
21
|
-
- TanStack/table:packages/table-core/src/fns/filterFns.ts
|
|
22
|
-
- TanStack/table:packages/table-core/src/fns/sortFns.ts
|
|
23
|
-
- TanStack/table:packages/table-core/src/fns/aggregationFns.ts
|
|
24
|
-
- TanStack/table:examples/react/filters-fuzzy/src/main.tsx
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
This skill builds on `tanstack-table/state-management`. Read it first for how feature plugins drive state slices.
|
|
28
|
-
|
|
29
|
-
## Setup
|
|
30
|
-
|
|
31
|
-
v9 customization happens in three places:
|
|
32
|
-
|
|
33
|
-
1. **Built-in function registries** — `filterFns`, `sortFns`, `aggregationFns` — registered as named slots on `tableFeatures({...})` so unused fns tree-shake away.
|
|
34
|
-
2. **Per-column overrides** — `columnDef.filterFn`, `columnDef.sortFn`, `columnDef.aggregationFn` (string name OR inline function).
|
|
35
|
-
3. **Table-level overrides** — `tableOptions.globalFilterFn`.
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
import {
|
|
39
|
-
tableFeatures,
|
|
40
|
-
rowSortingFeature,
|
|
41
|
-
columnFilteringFeature,
|
|
42
|
-
globalFilteringFeature,
|
|
43
|
-
columnGroupingFeature,
|
|
44
|
-
rowExpandingFeature,
|
|
45
|
-
createFilteredRowModel,
|
|
46
|
-
createSortedRowModel,
|
|
47
|
-
createGroupedRowModel,
|
|
48
|
-
createExpandedRowModel,
|
|
49
|
-
filterFns,
|
|
50
|
-
sortFns,
|
|
51
|
-
aggregationFns,
|
|
52
|
-
metaHelper,
|
|
53
|
-
createColumnHelper,
|
|
54
|
-
} from '@tanstack/table-core'
|
|
55
|
-
import type {
|
|
56
|
-
FilterFn,
|
|
57
|
-
SortFn,
|
|
58
|
-
AggregationFn,
|
|
59
|
-
TableFeatures,
|
|
60
|
-
} from '@tanstack/table-core'
|
|
61
|
-
import {
|
|
62
|
-
rankItem,
|
|
63
|
-
compareItems,
|
|
64
|
-
type RankingInfo,
|
|
65
|
-
} from '@tanstack/match-sorter-utils'
|
|
66
|
-
|
|
67
|
-
type Person = {
|
|
68
|
-
id: string
|
|
69
|
-
firstName: string
|
|
70
|
-
lastName: string
|
|
71
|
-
revenue: number
|
|
72
|
-
status: 'single' | 'complicated' | 'relationship'
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 1. Describe meta shape for the fuzzy filter.
|
|
76
|
-
interface FuzzyFilterMeta {
|
|
77
|
-
itemRank?: RankingInfo
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 2. Extend TableFeatures so FilterFn / SortFn can reference filterMeta types.
|
|
81
|
-
type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta }
|
|
82
|
-
|
|
83
|
-
const fuzzyFilter: FilterFn<FuzzyFeatures, Person> = (
|
|
84
|
-
row,
|
|
85
|
-
columnId,
|
|
86
|
-
value,
|
|
87
|
-
addMeta,
|
|
88
|
-
) => {
|
|
89
|
-
const itemRank = rankItem(row.getValue(columnId), value)
|
|
90
|
-
addMeta?.({ itemRank })
|
|
91
|
-
return itemRank.passed
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 3. Register everything on features — fn registries are named slots.
|
|
95
|
-
const features = tableFeatures({
|
|
96
|
-
rowSortingFeature,
|
|
97
|
-
columnFilteringFeature,
|
|
98
|
-
globalFilteringFeature,
|
|
99
|
-
columnGroupingFeature,
|
|
100
|
-
rowExpandingFeature,
|
|
101
|
-
filteredRowModel: createFilteredRowModel(),
|
|
102
|
-
sortedRowModel: createSortedRowModel(),
|
|
103
|
-
groupedRowModel: createGroupedRowModel(),
|
|
104
|
-
expandedRowModel: createExpandedRowModel(),
|
|
105
|
-
filterFns: { ...filterFns, fuzzy: fuzzyFilter }, // keep built-ins + add custom
|
|
106
|
-
sortFns,
|
|
107
|
-
aggregationFns,
|
|
108
|
-
filterMeta: metaHelper<FuzzyFilterMeta>(),
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
const columnHelper = createColumnHelper<typeof features, Person>()
|
|
112
|
-
|
|
113
|
-
const columns = columnHelper.columns([
|
|
114
|
-
columnHelper.accessor('firstName', {
|
|
115
|
-
filterFn: 'fuzzy', // ← string ref typechecks because 'fuzzy' is in features.filterFns
|
|
116
|
-
sortFn: 'alphanumeric',
|
|
117
|
-
}),
|
|
118
|
-
columnHelper.accessor('revenue', {
|
|
119
|
-
aggregationFn: 'sum',
|
|
120
|
-
aggregatedCell: (info) => `$${info.getValue<number>().toLocaleString()}`,
|
|
121
|
-
}),
|
|
122
|
-
])
|
|
123
|
-
|
|
124
|
-
const table = constructTable({
|
|
125
|
-
features,
|
|
126
|
-
columns,
|
|
127
|
-
data,
|
|
128
|
-
globalFilterFn: 'fuzzy',
|
|
129
|
-
})
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Core Patterns
|
|
133
|
-
|
|
134
|
-
### Pick a built-in `sortFn` by name + direction control
|
|
135
|
-
|
|
136
|
-
```ts
|
|
137
|
-
columnHelper.accessor('lastName', {
|
|
138
|
-
sortFn: 'alphanumeric',
|
|
139
|
-
sortDescFirst: false,
|
|
140
|
-
sortUndefined: 'last', // ABSOLUTE: always at end regardless of asc/desc
|
|
141
|
-
})
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Layered direction controls:
|
|
145
|
-
|
|
146
|
-
- `sortDescFirst: true/false` — first click sorts descending
|
|
147
|
-
- `sortUndefined: 'first' | 'last' | -1 | 1 | false` — string forms are absolute; numeric flips with `desc`
|
|
148
|
-
- `invertSorting: true` — for "lower-is-better" scales (rank 1 above rank 2 even when descending)
|
|
149
|
-
|
|
150
|
-
### Filter → sort handoff via `addMeta`
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
const fuzzyFilter: FilterFn<typeof features, Person> = (
|
|
154
|
-
row,
|
|
155
|
-
columnId,
|
|
156
|
-
value,
|
|
157
|
-
addMeta,
|
|
158
|
-
) => {
|
|
159
|
-
const itemRank = rankItem(row.getValue(columnId), value)
|
|
160
|
-
addMeta?.({ itemRank })
|
|
161
|
-
return itemRank.passed
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Custom sortFn reads the meta the filter stashed
|
|
165
|
-
const fuzzySort: SortFn<typeof features, Person> = (rowA, rowB, columnId) => {
|
|
166
|
-
let dir = 0
|
|
167
|
-
if (rowA.columnFiltersMeta[columnId]) {
|
|
168
|
-
dir = compareItems(
|
|
169
|
-
rowA.columnFiltersMeta[columnId].itemRank!,
|
|
170
|
-
rowB.columnFiltersMeta[columnId].itemRank!,
|
|
171
|
-
)
|
|
172
|
-
}
|
|
173
|
-
return dir === 0 ? sortFns.alphanumeric(rowA, rowB, columnId) : dir
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
columnHelper.accessor('fullName', { filterFn: 'fuzzy', sortFn: fuzzySort })
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
`row.columnFiltersMeta` is keyed by the column id that produced the meta (or `'__global__'` for the global filter). The sortFn MUST look up the same column id its filterFn used.
|
|
180
|
-
|
|
181
|
-
### Custom `aggregationFn` for grouping
|
|
182
|
-
|
|
183
|
-
```ts
|
|
184
|
-
import type { AggregationFn } from '@tanstack/table-core'
|
|
185
|
-
|
|
186
|
-
// Signature: (columnId, leafRows, childRows) → aggregated value
|
|
187
|
-
// leafRows = all descendant non-grouped rows
|
|
188
|
-
// childRows = immediate children (may be sub-aggregates at deeper levels)
|
|
189
|
-
const weightedMean: AggregationFn<typeof features, Person> = (
|
|
190
|
-
columnId,
|
|
191
|
-
leafRows,
|
|
192
|
-
) => {
|
|
193
|
-
let totalWeight = 0
|
|
194
|
-
let weightedSum = 0
|
|
195
|
-
leafRows.forEach((row) => {
|
|
196
|
-
const v = row.getValue<number>(columnId)
|
|
197
|
-
const w = row.original.revenue
|
|
198
|
-
weightedSum += v * w
|
|
199
|
-
totalWeight += w
|
|
200
|
-
})
|
|
201
|
-
return totalWeight === 0 ? 0 : weightedSum / totalWeight
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const customFeatures = tableFeatures({
|
|
205
|
-
columnGroupingFeature,
|
|
206
|
-
rowExpandingFeature,
|
|
207
|
-
groupedRowModel: createGroupedRowModel(),
|
|
208
|
-
expandedRowModel: createExpandedRowModel(),
|
|
209
|
-
aggregationFns: { ...aggregationFns, weightedMean },
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
const table = constructTable({
|
|
213
|
-
features: customFeatures,
|
|
214
|
-
columns: columnHelper.columns([
|
|
215
|
-
columnHelper.accessor('revenue', {
|
|
216
|
-
aggregationFn: 'weightedMean',
|
|
217
|
-
aggregatedCell: (info) => `$${info.getValue<number>().toFixed(2)}`,
|
|
218
|
-
}),
|
|
219
|
-
]),
|
|
220
|
-
data,
|
|
221
|
-
})
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## Common Mistakes
|
|
225
|
-
|
|
226
|
-
### [CRITICAL] Referencing a custom `filterFn` by string without registering it
|
|
227
|
-
|
|
228
|
-
Wrong:
|
|
229
|
-
|
|
230
|
-
```ts
|
|
231
|
-
// "fuzzy" string never registered in features.filterFns
|
|
232
|
-
const features = tableFeatures({
|
|
233
|
-
columnFilteringFeature,
|
|
234
|
-
filteredRowModel: createFilteredRowModel(),
|
|
235
|
-
filterFns, // ❌ built-ins only — no fuzzy
|
|
236
|
-
})
|
|
237
|
-
const table = useTable({
|
|
238
|
-
features,
|
|
239
|
-
columns: [columnHelper.accessor('fullName', { filterFn: 'fuzzy' })],
|
|
240
|
-
data,
|
|
241
|
-
})
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
Correct:
|
|
245
|
-
|
|
246
|
-
```ts
|
|
247
|
-
const fuzzyFilter: FilterFn<FuzzyFeatures, Person> = (
|
|
248
|
-
row,
|
|
249
|
-
columnId,
|
|
250
|
-
value,
|
|
251
|
-
addMeta,
|
|
252
|
-
) => {
|
|
253
|
-
const itemRank = rankItem(row.getValue(columnId), value)
|
|
254
|
-
addMeta?.({ itemRank })
|
|
255
|
-
return itemRank.passed
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const features = tableFeatures({
|
|
259
|
-
columnFilteringFeature,
|
|
260
|
-
filteredRowModel: createFilteredRowModel(),
|
|
261
|
-
filterFns: { ...filterFns, fuzzy: fuzzyFilter }, // ✅ registered
|
|
262
|
-
filterMeta: metaHelper<FuzzyFilterMeta>(),
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
const table = useTable({
|
|
266
|
-
features,
|
|
267
|
-
columns: [columnHelper.accessor('fullName', { filterFn: 'fuzzy' })],
|
|
268
|
-
data,
|
|
269
|
-
})
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
String values are looked up in `features.filterFns`. Unregistered names log `Could not find a valid 'column.filterFn' …` in dev and silently no-op in prod.
|
|
273
|
-
|
|
274
|
-
Source: examples/react/filters-fuzzy/src/main.tsx; packages/table-core/src/features/column-filtering/columnFilteringFeature.utils.ts
|
|
275
|
-
|
|
276
|
-
### [HIGH] Using v8 `sortingFn` / `sortingFns` names
|
|
277
|
-
|
|
278
|
-
Wrong:
|
|
279
|
-
|
|
280
|
-
```ts
|
|
281
|
-
columnHelper.accessor('age', {
|
|
282
|
-
sortingFn: 'alphanumeric', // v8 name — ignored
|
|
283
|
-
})
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
Correct:
|
|
287
|
-
|
|
288
|
-
```ts
|
|
289
|
-
columnHelper.accessor('age', {
|
|
290
|
-
sortFn: 'alphanumeric',
|
|
291
|
-
})
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
v9 renamed every sorting API: `sortingFn` → `sortFn`, `sortingFns` → `sortFns`, type `SortingFn` → `SortFn`, `column.getSortingFn()` → `column.getSortFn()`. The default `sortFn` is `'auto'`, falling back to `sortFn_basic` if the lookup misses — so wrong names sort wrong instead of erroring.
|
|
295
|
-
|
|
296
|
-
Source: docs/framework/react/guide/migrating.md; packages/table-core/src/features/row-sorting/rowSortingFeature.types.ts
|
|
297
|
-
|
|
298
|
-
### [HIGH] Custom `sortFn` reads filter meta from a different column id
|
|
299
|
-
|
|
300
|
-
Wrong:
|
|
301
|
-
|
|
302
|
-
```ts
|
|
303
|
-
// filter on 'fullName', sort reads meta from 'firstName'
|
|
304
|
-
const fuzzySort: SortFn<typeof features, Person> = (a, b, columnId) => {
|
|
305
|
-
const meta = a.columnFiltersMeta['firstName'] // ❌ wrong key
|
|
306
|
-
return meta
|
|
307
|
-
? compareItems(meta.itemRank, b.columnFiltersMeta['firstName'].itemRank)
|
|
308
|
-
: 0
|
|
309
|
-
}
|
|
310
|
-
columnHelper.accessor('fullName', { filterFn: 'fuzzy', sortFn: fuzzySort })
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
Correct:
|
|
314
|
-
|
|
315
|
-
```ts
|
|
316
|
-
const fuzzySort: SortFn<typeof features, Person> = (rowA, rowB, columnId) => {
|
|
317
|
-
let dir = 0
|
|
318
|
-
if (rowA.columnFiltersMeta[columnId]) {
|
|
319
|
-
dir = compareItems(
|
|
320
|
-
rowA.columnFiltersMeta[columnId].itemRank!,
|
|
321
|
-
rowB.columnFiltersMeta[columnId].itemRank!,
|
|
322
|
-
)
|
|
323
|
-
}
|
|
324
|
-
return dir === 0 ? sortFns.alphanumeric(rowA, rowB, columnId) : dir
|
|
325
|
-
}
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
`row.columnFiltersMeta` is keyed by the column id that produced it. Always use the `columnId` argument the sortFn receives.
|
|
329
|
-
|
|
330
|
-
Source: examples/react/filters-fuzzy/src/main.tsx
|
|
331
|
-
|
|
332
|
-
### [MEDIUM] Returning a complex value from the accessor while using a built-in `sortFn`
|
|
333
|
-
|
|
334
|
-
Wrong:
|
|
335
|
-
|
|
336
|
-
```ts
|
|
337
|
-
// accessor returns object; alphanumeric sees "[object Object]"
|
|
338
|
-
columnHelper.accessor((row) => row.name, {
|
|
339
|
-
id: 'name',
|
|
340
|
-
sortFn: 'alphanumeric',
|
|
341
|
-
})
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
Correct:
|
|
345
|
-
|
|
346
|
-
```ts
|
|
347
|
-
// Option A — return a primitive
|
|
348
|
-
columnHelper.accessor((row) => `${row.name.first} ${row.name.last}`, {
|
|
349
|
-
id: 'fullName',
|
|
350
|
-
sortFn: 'alphanumeric',
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
// Option B — custom sortFn that knows the shape
|
|
354
|
-
columnHelper.accessor((row) => row.name, {
|
|
355
|
-
id: 'name',
|
|
356
|
-
sortFn: (a, b, id) => {
|
|
357
|
-
const av = a.getValue<{ first: string }>(id).first
|
|
358
|
-
const bv = b.getValue<{ first: string }>(id).first
|
|
359
|
-
return av === bv ? 0 : av > bv ? 1 : -1
|
|
360
|
-
},
|
|
361
|
-
})
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
Built-in sortFns (`alphanumeric`, `text`, `basic`) coerce via comparison operators. Object accessors collapse to `"[object Object]"` and every row ties.
|
|
365
|
-
|
|
366
|
-
Source: packages/table-core/src/fns/sortFns.ts
|
|
367
|
-
|
|
368
|
-
### [MEDIUM] Confusing `aggregationFn` with `aggregatedCell`
|
|
369
|
-
|
|
370
|
-
Wrong:
|
|
371
|
-
|
|
372
|
-
```ts
|
|
373
|
-
// rendering JSX inside the aggregation function
|
|
374
|
-
columnHelper.accessor('revenue', {
|
|
375
|
-
aggregationFn: (id, leaves) => <b>${leaves.reduce((a, r) => a + r.getValue(id), 0)}</b>,
|
|
376
|
-
})
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
Correct:
|
|
380
|
-
|
|
381
|
-
```ts
|
|
382
|
-
columnHelper.accessor('revenue', {
|
|
383
|
-
aggregationFn: 'sum', // returns a value
|
|
384
|
-
aggregatedCell: (info) => <b>${info.getValue<number>().toLocaleString()}</b>, // renders it
|
|
385
|
-
})
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
`aggregationFn` produces the grouped-row value (signature `(columnId, leafRows, childRows)`). `aggregatedCell` renders it. Don't combine.
|
|
389
|
-
|
|
390
|
-
Source: packages/table-core/src/features/column-grouping/columnGroupingFeature.types.ts
|
|
391
|
-
|
|
392
|
-
### [CRITICAL] Reimplementing what built-in APIs provide
|
|
393
|
-
|
|
394
|
-
Wrong:
|
|
395
|
-
|
|
396
|
-
```ts
|
|
397
|
-
// Reimplements sorting state manually instead of using the API
|
|
398
|
-
const [sorting, setSorting] = useState([])
|
|
399
|
-
const sortedData = useMemo(() => [...data].sort(/* … */), [data, sorting])
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
Correct:
|
|
403
|
-
|
|
404
|
-
```ts
|
|
405
|
-
const table = useTable({
|
|
406
|
-
features: tableFeatures({
|
|
407
|
-
rowSortingFeature,
|
|
408
|
-
sortedRowModel: createSortedRowModel(),
|
|
409
|
-
sortFns,
|
|
410
|
-
}),
|
|
411
|
-
columns,
|
|
412
|
-
data,
|
|
413
|
-
})
|
|
414
|
-
// table.setSorting(...), column.toggleSorting(), header.getToggleSortingHandler()
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
Source: maintainer interview (Phase 4, 2026-05-17)
|
|
418
|
-
|
|
419
|
-
## See also
|
|
420
|
-
|
|
421
|
-
- `tanstack-table/filtering` — `filterFn` placement, fuzzy filter pattern, faceted UI
|
|
422
|
-
- `tanstack-table/sorting` — built-in `sortFns`, multi-sort, `sortUndefined`
|
|
423
|
-
- `tanstack-table/grouping` — `aggregationFn` signature details and built-ins
|