@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.
Files changed (100) hide show
  1. package/README.md +1 -0
  2. package/dist/core/headers/buildHeaderGroups.cjs.map +1 -1
  3. package/dist/core/headers/buildHeaderGroups.d.cts +1 -1
  4. package/dist/core/headers/buildHeaderGroups.d.ts +1 -1
  5. package/dist/core/headers/buildHeaderGroups.js.map +1 -1
  6. package/dist/core/headers/coreHeadersFeature.utils.cjs +7 -7
  7. package/dist/core/headers/coreHeadersFeature.utils.cjs.map +1 -1
  8. package/dist/core/headers/coreHeadersFeature.utils.js +7 -7
  9. package/dist/core/headers/coreHeadersFeature.utils.js.map +1 -1
  10. package/dist/core/table/coreTablesFeature.utils.cjs +1 -1
  11. package/dist/core/table/coreTablesFeature.utils.cjs.map +1 -1
  12. package/dist/core/table/coreTablesFeature.utils.js +1 -1
  13. package/dist/core/table/coreTablesFeature.utils.js.map +1 -1
  14. package/dist/features/column-ordering/columnOrderingFeature.types.d.cts +5 -5
  15. package/dist/features/column-ordering/columnOrderingFeature.types.d.ts +5 -5
  16. package/dist/features/column-ordering/columnOrderingFeature.utils.cjs +6 -6
  17. package/dist/features/column-ordering/columnOrderingFeature.utils.cjs.map +1 -1
  18. package/dist/features/column-ordering/columnOrderingFeature.utils.d.cts +3 -3
  19. package/dist/features/column-ordering/columnOrderingFeature.utils.d.ts +3 -3
  20. package/dist/features/column-ordering/columnOrderingFeature.utils.js +6 -6
  21. package/dist/features/column-ordering/columnOrderingFeature.utils.js.map +1 -1
  22. package/dist/features/column-pinning/columnPinningFeature.cjs +44 -39
  23. package/dist/features/column-pinning/columnPinningFeature.cjs.map +1 -1
  24. package/dist/features/column-pinning/columnPinningFeature.d.cts +6 -1
  25. package/dist/features/column-pinning/columnPinningFeature.d.ts +6 -1
  26. package/dist/features/column-pinning/columnPinningFeature.js +45 -40
  27. package/dist/features/column-pinning/columnPinningFeature.js.map +1 -1
  28. package/dist/features/column-pinning/columnPinningFeature.types.d.cts +49 -38
  29. package/dist/features/column-pinning/columnPinningFeature.types.d.ts +49 -38
  30. package/dist/features/column-pinning/columnPinningFeature.utils.cjs +154 -146
  31. package/dist/features/column-pinning/columnPinningFeature.utils.cjs.map +1 -1
  32. package/dist/features/column-pinning/columnPinningFeature.utils.d.cts +81 -73
  33. package/dist/features/column-pinning/columnPinningFeature.utils.d.ts +81 -73
  34. package/dist/features/column-pinning/columnPinningFeature.utils.js +141 -133
  35. package/dist/features/column-pinning/columnPinningFeature.utils.js.map +1 -1
  36. package/dist/features/column-sizing/columnSizingFeature.cjs +4 -4
  37. package/dist/features/column-sizing/columnSizingFeature.cjs.map +1 -1
  38. package/dist/features/column-sizing/columnSizingFeature.js +5 -5
  39. package/dist/features/column-sizing/columnSizingFeature.js.map +1 -1
  40. package/dist/features/column-sizing/columnSizingFeature.types.d.cts +18 -12
  41. package/dist/features/column-sizing/columnSizingFeature.types.d.ts +18 -12
  42. package/dist/features/column-sizing/columnSizingFeature.utils.cjs +24 -20
  43. package/dist/features/column-sizing/columnSizingFeature.utils.cjs.map +1 -1
  44. package/dist/features/column-sizing/columnSizingFeature.utils.d.cts +18 -14
  45. package/dist/features/column-sizing/columnSizingFeature.utils.d.ts +18 -14
  46. package/dist/features/column-sizing/columnSizingFeature.utils.js +24 -20
  47. package/dist/features/column-sizing/columnSizingFeature.utils.js.map +1 -1
  48. package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs +16 -16
  49. package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs.map +1 -1
  50. package/dist/features/column-visibility/columnVisibilityFeature.utils.d.cts +3 -3
  51. package/dist/features/column-visibility/columnVisibilityFeature.utils.d.ts +3 -3
  52. package/dist/features/column-visibility/columnVisibilityFeature.utils.js +16 -16
  53. package/dist/features/column-visibility/columnVisibilityFeature.utils.js.map +1 -1
  54. package/dist/static-functions.cjs +16 -16
  55. package/dist/static-functions.d.cts +3 -3
  56. package/dist/static-functions.d.ts +3 -3
  57. package/dist/static-functions.js +3 -3
  58. package/package.json +1 -1
  59. package/skills/api-not-found/SKILL.md +113 -0
  60. package/skills/client-vs-server/SKILL.md +164 -0
  61. package/skills/column-faceting/SKILL.md +91 -0
  62. package/skills/column-filtering/SKILL.md +82 -0
  63. package/skills/column-ordering/SKILL.md +75 -0
  64. package/skills/column-pinning/SKILL.md +89 -0
  65. package/skills/column-resizing/SKILL.md +91 -0
  66. package/skills/column-sizing/SKILL.md +72 -0
  67. package/skills/column-visibility/SKILL.md +75 -0
  68. package/skills/core/SKILL.md +140 -0
  69. package/skills/custom-features/SKILL.md +207 -0
  70. package/skills/expanding/SKILL.md +80 -0
  71. package/skills/global-filtering/SKILL.md +84 -0
  72. package/skills/grouping/SKILL.md +50 -394
  73. package/skills/migrate-v8-to-v9/SKILL.md +230 -390
  74. package/skills/pagination/SKILL.md +35 -344
  75. package/skills/row-pinning/SKILL.md +47 -238
  76. package/skills/row-selection/SKILL.md +39 -351
  77. package/skills/sorting/SKILL.md +35 -299
  78. package/skills/table-features/SKILL.md +153 -0
  79. package/skills/typescript/SKILL.md +126 -0
  80. package/src/core/headers/buildHeaderGroups.ts +1 -1
  81. package/src/core/headers/coreHeadersFeature.utils.ts +7 -7
  82. package/src/core/table/coreTablesFeature.utils.ts +1 -1
  83. package/src/features/column-ordering/columnOrderingFeature.types.ts +5 -5
  84. package/src/features/column-ordering/columnOrderingFeature.utils.ts +9 -9
  85. package/src/features/column-pinning/columnPinningFeature.ts +64 -59
  86. package/src/features/column-pinning/columnPinningFeature.types.ts +49 -38
  87. package/src/features/column-pinning/columnPinningFeature.utils.ts +163 -155
  88. package/src/features/column-sizing/columnSizingFeature.ts +6 -6
  89. package/src/features/column-sizing/columnSizingFeature.types.ts +18 -12
  90. package/src/features/column-sizing/columnSizingFeature.utils.ts +31 -27
  91. package/src/features/column-visibility/columnVisibilityFeature.utils.ts +15 -15
  92. package/skills/column-definitions/SKILL.md +0 -330
  93. package/skills/column-layout/SKILL.md +0 -326
  94. package/skills/column-layout/references/subsystems.md +0 -220
  95. package/skills/customizing-feature-behavior/SKILL.md +0 -423
  96. package/skills/filtering/SKILL.md +0 -375
  97. package/skills/filtering/references/faceting-and-fuzzy.md +0 -218
  98. package/skills/row-expanding/SKILL.md +0 -356
  99. package/skills/setup/SKILL.md +0 -390
  100. package/skills/state-management/SKILL.md +0 -403
@@ -1,375 +0,0 @@
1
- ---
2
- name: filtering
3
- description: >
4
- Filter rows in TanStack Table v9 with the `filteredRowModel` pipeline stage.
5
- Covers `columnFilteringFeature` + `globalFilteringFeature` + `columnFacetingFeature`,
6
- `createFilteredRowModel()` (registered on `features` with `filterFns` slot), `createFacetedRowModel()` /
7
- `createFacetedUniqueValues()` / `createFacetedMinMaxValues()`, fuzzy filtering
8
- with `@tanstack/match-sorter-utils`, the built-in `filterFns` registry, custom
9
- `filterFn` + module augmentation, `state.columnFilters` (Array<{ id, value }>),
10
- `state.globalFilter`, `column.setFilterValue` / `setColumnFilters` /
11
- `setGlobalFilter`, `column.getFacetedUniqueValues` /
12
- `column.getFacetedMinMaxValues`, `manualFiltering`, `filterFromLeafRows`,
13
- `maxLeafRowFilterDepth`, `getColumnCanGlobalFilter`. Five subsystems:
14
- column-filtering, global-filtering, column-faceting, global-faceting,
15
- fuzzy-filtering.
16
- type: core
17
- library: tanstack-table
18
- library_version: '9.0.0-alpha.48'
19
- requires:
20
- - state-management
21
- - customizing-feature-behavior
22
- sources:
23
- - TanStack/table:docs/guide/column-filtering.md
24
- - TanStack/table:docs/guide/global-filtering.md
25
- - TanStack/table:docs/guide/column-faceting.md
26
- - TanStack/table:docs/guide/global-faceting.md
27
- - TanStack/table:docs/guide/fuzzy-filtering.md
28
- - TanStack/table:examples/react/filters/src/main.tsx
29
- - TanStack/table:examples/react/filters-faceted/src/main.tsx
30
- - TanStack/table:examples/react/filters-fuzzy/src/main.tsx
31
- ---
32
-
33
- This skill builds on `tanstack-table/state-management` and `tanstack-table/customizing-feature-behavior`. Read those first for the atom model and `filterFn`/`globalFilterFn` overrides.
34
-
35
- ## Setup
36
-
37
- Filtering has five subsystems in v9 — register only the features you need:
38
-
39
- | Subsystem | Feature | Row-model |
40
- | ---------------- | ------------------------ | --------------------------------------------- |
41
- | column-filtering | `columnFilteringFeature` | `createFilteredRowModel()` + `filterFns` slot |
42
- | global-filtering | `globalFilteringFeature` | (same `filteredRowModel`) |
43
- | column-faceting | `columnFacetingFeature` | `createFacetedRowModel()` + helpers |
44
- | global-faceting | `globalFacetingFeature` | global versions of the helpers |
45
- | fuzzy-filtering | (custom filterFn) | `@tanstack/match-sorter-utils` |
46
-
47
- ```ts
48
- import {
49
- tableFeatures,
50
- columnFilteringFeature,
51
- globalFilteringFeature,
52
- rowPaginationFeature,
53
- createFilteredRowModel,
54
- createPaginatedRowModel,
55
- filterFns,
56
- } from '@tanstack/table-core'
57
- import type { ColumnFiltersState } from '@tanstack/table-core'
58
-
59
- const features = tableFeatures({
60
- columnFilteringFeature,
61
- globalFilteringFeature,
62
- rowPaginationFeature,
63
- filteredRowModel: createFilteredRowModel(),
64
- paginatedRowModel: createPaginatedRowModel(),
65
- filterFns,
66
- })
67
-
68
- const table = constructTable({
69
- features,
70
- columns,
71
- data,
72
- initialState: {
73
- columnFilters: [] satisfies ColumnFiltersState,
74
- globalFilter: '',
75
- },
76
- globalFilterFn: 'includesString',
77
- })
78
-
79
- table.setColumnFilters([{ id: 'firstName', value: 'Ada' }])
80
- table.setGlobalFilter('Lovelace')
81
- ```
82
-
83
- ## Core Patterns
84
-
85
- ### Text / range / select column filters
86
-
87
- ```tsx
88
- // From examples/react/filters/src/main.tsx
89
- function Filter({ column }) {
90
- const firstValue = column
91
- .getFacetedRowModel()
92
- .flatRows[0]?.getValue(column.id)
93
- const columnFilterValue = column.getFilterValue()
94
-
95
- return typeof firstValue === 'number' ? (
96
- <div>
97
- <input
98
- type="number"
99
- value={(columnFilterValue as [number, number])?.[0] ?? ''}
100
- onChange={(e) =>
101
- column.setFilterValue((old: [number, number]) => [
102
- e.target.value,
103
- old?.[1],
104
- ])
105
- }
106
- placeholder="Min"
107
- />
108
- <input
109
- type="number"
110
- value={(columnFilterValue as [number, number])?.[1] ?? ''}
111
- onChange={(e) =>
112
- column.setFilterValue((old: [number, number]) => [
113
- old?.[0],
114
- e.target.value,
115
- ])
116
- }
117
- placeholder="Max"
118
- />
119
- </div>
120
- ) : (
121
- <input
122
- value={(columnFilterValue ?? '') as string}
123
- onChange={(e) => column.setFilterValue(e.target.value)}
124
- placeholder="Search…"
125
- />
126
- )
127
- }
128
- ```
129
-
130
- ### Faceted filter UIs and fuzzy global search
131
-
132
- Faceting requires `createFacetedRowModel()` as the base plus `createFacetedUniqueValues()` / `createFacetedMinMaxValues()` — all registered on the `features` object; fuzzy filtering wires a custom `FilterFn` backed by `@tanstack/match-sorter-utils` (`rankItem` / `compareItems`) with the `filterFns` registry slot and `filterMeta` slot for type-safe meta. Full examples in [faceting-and-fuzzy.md](references/faceting-and-fuzzy.md).
133
-
134
- ### Server-side filtering
135
-
136
- ```tsx
137
- const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
138
- const { data } = useQuery({
139
- queryKey: ['rows', columnFilters],
140
- queryFn: () =>
141
- fetch('/api/rows?' + serialize(columnFilters)).then((r) => r.json()),
142
- })
143
- const table = useTable({
144
- features: tableFeatures({ columnFilteringFeature }),
145
- // no filteredRowModel registered — server filters
146
- data,
147
- columns,
148
- manualFiltering: true,
149
- state: { columnFilters },
150
- onColumnFiltersChange: setColumnFilters,
151
- })
152
- ```
153
-
154
- ### Filter tree data and keep matching descendants visible
155
-
156
- ```ts
157
- const features = tableFeatures({
158
- columnFilteringFeature,
159
- rowExpandingFeature,
160
- filteredRowModel: createFilteredRowModel(),
161
- expandedRowModel: createExpandedRowModel(),
162
- filterFns,
163
- })
164
- const table = constructTable({
165
- features,
166
- columns,
167
- data,
168
- getSubRows: (r) => r.subRows,
169
- filterFromLeafRows: true, // bottom-up: keep parent if any descendant matches
170
- })
171
- ```
172
-
173
- ## Common Mistakes
174
-
175
- ### [HIGH] Forgetting `createFacetedRowModel()` while registering `createFacetedUniqueValues()` / `createFacetedMinMaxValues()`
176
-
177
- Wrong:
178
-
179
- ```tsx
180
- const table = useTable({
181
- features: tableFeatures({
182
- columnFacetingFeature,
183
- columnFilteringFeature,
184
- filteredRowModel: createFilteredRowModel(),
185
- // BUG: missing facetedRowModel
186
- facetedUniqueValues: createFacetedUniqueValues(),
187
- facetedMinMaxValues: createFacetedMinMaxValues(),
188
- filterFns,
189
- }),
190
- columns,
191
- data,
192
- })
193
- ```
194
-
195
- Correct:
196
-
197
- ```tsx
198
- const table = useTable({
199
- features: tableFeatures({
200
- columnFacetingFeature,
201
- columnFilteringFeature,
202
- rowPaginationFeature,
203
- filteredRowModel: createFilteredRowModel(),
204
- paginatedRowModel: createPaginatedRowModel(),
205
- facetedRowModel: createFacetedRowModel(), // REQUIRED base
206
- facetedMinMaxValues: createFacetedMinMaxValues(),
207
- facetedUniqueValues: createFacetedUniqueValues(),
208
- filterFns,
209
- }),
210
- columns,
211
- data,
212
- })
213
- ```
214
-
215
- Without the base `facetedRowModel`, the unique/minMax helpers fall back to `getPreFilteredRowModel()` — facet values stop excluding the column's own active filter, and a select dropdown collapses to only the currently selected value once the user picks one.
216
-
217
- Source: packages/table-core/src/features/column-faceting/columnFacetingFeature.utils.ts; examples/react/filters-faceted/src/main.tsx
218
-
219
- ### [HIGH] Setting `manualFiltering: true` without refetching data
220
-
221
- Wrong:
222
-
223
- ```tsx
224
- // manualFiltering bypasses the filteredRowModel — filter UI changes do nothing
225
- const table = useTable({
226
- features: tableFeatures({
227
- columnFilteringFeature,
228
- filteredRowModel: createFilteredRowModel(),
229
- filterFns,
230
- }),
231
- data,
232
- columns,
233
- manualFiltering: true,
234
- // ...but no useEffect / useQuery key tracking columnFilters
235
- })
236
- ```
237
-
238
- Correct:
239
-
240
- ```tsx
241
- const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
242
- const { data } = useQuery({
243
- queryKey: ['rows', columnFilters],
244
- queryFn: () =>
245
- fetch('/api/rows?' + serialize(columnFilters)).then((r) => r.json()),
246
- })
247
-
248
- const table = useTable({
249
- features: tableFeatures({ columnFilteringFeature }),
250
- // no filteredRowModel registered for manual mode
251
- data,
252
- columns,
253
- manualFiltering: true,
254
- state: { columnFilters },
255
- onColumnFiltersChange: setColumnFilters,
256
- })
257
- ```
258
-
259
- With `manualFiltering: true`, `table_getFilteredRowModel` short-circuits and returns the core rows. Rows are NOT filtered client-side — you must refetch.
260
-
261
- Source: docs/guide/column-filtering.md; packages/table-core/src/core/row-models/coreRowModelsFeature.utils.ts
262
-
263
- ### [HIGH] Custom fuzzy filter without merging into `filterFns`
264
-
265
- Wrong:
266
-
267
- ```tsx
268
- // drops the built-in registry
269
- const features = tableFeatures({
270
- columnFilteringFeature,
271
- filteredRowModel: createFilteredRowModel(),
272
- filterFns: { fuzzy: fuzzyFilter }, // BUG: drops built-ins
273
- })
274
- // Column with filterFn: 'includesString' now warns and never filters
275
- ```
276
-
277
- Correct:
278
-
279
- ```tsx
280
- import { filterFns } from '@tanstack/react-table'
281
-
282
- const features = tableFeatures({
283
- columnFilteringFeature,
284
- filteredRowModel: createFilteredRowModel(),
285
- filterFns: {
286
- ...filterFns, // KEEP built-ins
287
- fuzzy: fuzzyFilter, // ADD custom
288
- },
289
- })
290
- ```
291
-
292
- The `filterFns` slot replaces the registry with whatever you provide. Any column using a built-in name like `'includesString'` becomes a no-op if the built-ins are dropped.
293
-
294
- Source: examples/react/filters-fuzzy/src/main.tsx
295
-
296
- ### [MEDIUM] Global filter silently skips columns with non-string/non-number values
297
-
298
- Wrong:
299
-
300
- ```tsx
301
- // createdAt is a Date object — global filter silently skips it
302
- const columns = [
303
- columnHelper.accessor('createdAt', { header: 'Created' }),
304
- columnHelper.accessor('name', { header: 'Name' }),
305
- ]
306
- // table.setGlobalFilter('2024') will never find Date rows
307
- ```
308
-
309
- Correct:
310
-
311
- ```tsx
312
- const table = useTable({
313
- features: tableFeatures({
314
- globalFilteringFeature,
315
- filteredRowModel: createFilteredRowModel(),
316
- filterFns,
317
- }),
318
- columns,
319
- data,
320
- globalFilterFn: 'includesString',
321
- getColumnCanGlobalFilter: (column) => true, // include every column
322
- })
323
-
324
- // Or per-column:
325
- columnHelper.accessor('createdAt', {
326
- header: 'Created',
327
- enableGlobalFilter: true,
328
- })
329
- ```
330
-
331
- `globalFilteringFeature` defaults `getColumnCanGlobalFilter` to a function that returns `typeof value === 'string' || typeof value === 'number'` sampled from the first row. Objects, dates, booleans, undefined all silently fail.
332
-
333
- Source: packages/table-core/src/features/global-filtering/globalFilteringFeature.ts
334
-
335
- ### [CRITICAL] Reimplementing what built-in APIs provide
336
-
337
- Wrong:
338
-
339
- ```ts
340
- // Hand-rolled filter loop, bypassing the table
341
- const filteredData = useMemo(
342
- () => data.filter(/* …custom matching… */),
343
- [data, query],
344
- )
345
- ```
346
-
347
- Correct:
348
-
349
- ```ts
350
- const table = useTable({
351
- features: tableFeatures({
352
- columnFilteringFeature,
353
- filteredRowModel: createFilteredRowModel(),
354
- filterFns,
355
- }),
356
- columns,
357
- data,
358
- })
359
- table.setColumnFilters([{ id: 'name', value: 'Ada' }])
360
- // or: column.setFilterValue('Ada')
361
- ```
362
-
363
- `table.setColumnFilters`, `column.setFilterValue`, `table.setGlobalFilter` honor reset behavior and internal invariants.
364
-
365
- Source: maintainer interview (Phase 4, 2026-05-17)
366
-
367
- ## See also
368
-
369
- - `tanstack-table/customizing-feature-behavior` — `filterFn` / `globalFilterFn` authoring, `addMeta` chain
370
- - `tanstack-table/sorting` — fuzzy sort pairing for `match-sorter-utils`
371
- - `tanstack-table/row-expanding` — `filterFromLeafRows` interaction with tree data
372
-
373
- ## References
374
-
375
- - [faceting-and-fuzzy.md](references/faceting-and-fuzzy.md) — faceted filter UIs (autocomplete + range slider) with `createFacetedRowModel`/`createFacetedUniqueValues`/`createFacetedMinMaxValues`, fuzzy global search via `@tanstack/match-sorter-utils`, plus MEDIUM-priority failure modes: `state` + `initialState` collision, `filterFromLeafRows` semantics, `'auto'` filter misdetection on null first row
@@ -1,218 +0,0 @@
1
- # Faceting and fuzzy filtering — extended patterns
2
-
3
- Extended filtering patterns extracted from `SKILL.md`. The SKILL keeps simple column filter, global filter, server-side, and tree-data patterns inline; this file covers faceted UIs and fuzzy filtering with `@tanstack/match-sorter-utils`.
4
-
5
- ## Faceted filter UIs (autocomplete + range slider)
6
-
7
- ```ts
8
- import {
9
- columnFacetingFeature,
10
- createFacetedRowModel,
11
- createFacetedUniqueValues,
12
- createFacetedMinMaxValues,
13
- } from '@tanstack/table-core'
14
-
15
- const features = tableFeatures({
16
- columnFacetingFeature,
17
- columnFilteringFeature,
18
- rowPaginationFeature,
19
- filteredRowModel: createFilteredRowModel(),
20
- paginatedRowModel: createPaginatedRowModel(),
21
- facetedRowModel: createFacetedRowModel(), // REQUIRED base
22
- facetedMinMaxValues: createFacetedMinMaxValues(),
23
- facetedUniqueValues: createFacetedUniqueValues(),
24
- filterFns,
25
- })
26
-
27
- const table = constructTable({
28
- features,
29
- columns,
30
- data,
31
- })
32
-
33
- // In a Filter component:
34
- const uniqueValues = column.getFacetedUniqueValues() // Map<value, count>
35
- const [min, max] = column.getFacetedMinMaxValues() ?? [0, 0]
36
- ```
37
-
38
- ## Fuzzy global search with match-sorter-utils
39
-
40
- ```ts
41
- import {
42
- rankItem,
43
- compareItems,
44
- type RankingInfo,
45
- } from '@tanstack/match-sorter-utils'
46
- import {
47
- tableFeatures,
48
- columnFilteringFeature,
49
- globalFilteringFeature,
50
- rowSortingFeature,
51
- createFilteredRowModel,
52
- createSortedRowModel,
53
- filterFns,
54
- sortFns,
55
- metaHelper,
56
- } from '@tanstack/table-core'
57
- import type { FilterFn, SortFn, TableFeatures } from '@tanstack/table-core'
58
-
59
- // 1. Describe the filterMeta shape for this feature set.
60
- interface FuzzyFilterMeta {
61
- itemRank?: RankingInfo
62
- }
63
-
64
- // 2. Extend TableFeatures so FilterFn / SortFn can read filterMeta types.
65
- type FuzzyFeatures = TableFeatures & { filterMeta: FuzzyFilterMeta }
66
-
67
- const fuzzyFilter: FilterFn<FuzzyFeatures, Person> = (
68
- row,
69
- columnId,
70
- value,
71
- addMeta,
72
- ) => {
73
- const itemRank = rankItem(row.getValue(columnId), value)
74
- addMeta?.({ itemRank })
75
- return itemRank.passed
76
- }
77
-
78
- const fuzzySort: SortFn<FuzzyFeatures, Person> = (rowA, rowB, columnId) => {
79
- let dir = 0
80
- if (rowA.columnFiltersMeta[columnId]) {
81
- dir = compareItems(
82
- rowA.columnFiltersMeta[columnId].itemRank!,
83
- rowB.columnFiltersMeta[columnId].itemRank!,
84
- )
85
- }
86
- return dir === 0 ? sortFns.alphanumeric(rowA, rowB, columnId) : dir
87
- }
88
-
89
- // 3. Register everything on features — no rowModels option needed.
90
- const features = tableFeatures({
91
- columnFilteringFeature,
92
- globalFilteringFeature,
93
- rowSortingFeature,
94
- filteredRowModel: createFilteredRowModel(),
95
- sortedRowModel: createSortedRowModel(),
96
- filterFns: { ...filterFns, fuzzy: fuzzyFilter },
97
- sortFns: { ...sortFns, fuzzy: fuzzySort },
98
- filterMeta: metaHelper<FuzzyFilterMeta>(),
99
- })
100
-
101
- const table = constructTable({
102
- features,
103
- columns,
104
- data,
105
- globalFilterFn: 'fuzzy',
106
- })
107
- ```
108
-
109
- Use string refs in column defs when the fn is registered:
110
-
111
- ```ts
112
- columnHelper.accessor('fullName', {
113
- filterFn: 'fuzzy', // string ref — typechecks because 'fuzzy' is in features.filterFns
114
- sortFn: 'fuzzy',
115
- })
116
- ```
117
-
118
- ## Additional MEDIUM-priority failure modes
119
-
120
- ### Using `state.columnFilters` AND `initialState.columnFilters` simultaneously
121
-
122
- Wrong:
123
-
124
- ```tsx
125
- const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
126
- const table = useTable({
127
- initialState: { columnFilters: [{ id: 'name', value: 'John' }] }, // IGNORED
128
- state: { columnFilters }, // wins, starts empty
129
- onColumnFiltersChange: setColumnFilters,
130
- })
131
- ```
132
-
133
- Correct:
134
-
135
- ```tsx
136
- // Seed the controlled state at useState time, NOT in initialState
137
- const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([
138
- { id: 'name', value: 'John' },
139
- ])
140
- const table = useTable({
141
- state: { columnFilters },
142
- onColumnFiltersChange: setColumnFilters,
143
- })
144
- ```
145
-
146
- `state` always overrides `initialState`. Seed your controlled state instead.
147
-
148
- Source: docs/guide/column-filtering.md
149
-
150
- ### Expecting `filterFromLeafRows` to keep ALL children of a matching parent visible
151
-
152
- Wrong:
153
-
154
- ```tsx
155
- // filterFromLeafRows hides children that don't match individually
156
- const table = useTable({
157
- features: tableFeatures({
158
- columnFilteringFeature,
159
- rowExpandingFeature,
160
- filteredRowModel: createFilteredRowModel(),
161
- expandedRowModel: createExpandedRowModel(),
162
- filterFns,
163
- }),
164
- columns,
165
- data,
166
- getSubRows: (r) => r.subRows,
167
- filterFromLeafRows: true,
168
- // expectation: parent matches "John" → all children visible
169
- // reality: only children that also match "John" stay visible
170
- })
171
- ```
172
-
173
- Correct:
174
-
175
- ```tsx
176
- // Filter root-only to preserve all sub-rows under a matching parent
177
- const table = useTable({
178
- features: tableFeatures({
179
- columnFilteringFeature,
180
- rowExpandingFeature,
181
- filteredRowModel: createFilteredRowModel(),
182
- expandedRowModel: createExpandedRowModel(),
183
- filterFns,
184
- }),
185
- columns,
186
- data,
187
- getSubRows: (r) => r.subRows,
188
- maxLeafRowFilterDepth: 0,
189
- })
190
- ```
191
-
192
- `filterFromLeafRows: true` is bottom-up. The mutually exclusive top-down default is what preserves descendants under a matching parent.
193
-
194
- Source: packages/table-core/src/features/column-filtering/filterRowsUtils.ts
195
-
196
- ### Auto filter type misdetects when first row has `null`/`undefined`
197
-
198
- Wrong:
199
-
200
- ```ts
201
- const data = [
202
- { id: 1, name: null }, // first row
203
- { id: 2, name: 'Alice' },
204
- ]
205
- // Column filter returns 0 results — auto picked wrong filter from null
206
- ```
207
-
208
- Correct:
209
-
210
- ```ts
211
- columnHelper.accessor('name', {
212
- filterFn: 'includesString', // explicit, don't rely on auto
213
- })
214
- ```
215
-
216
- The default `'auto'` `filterFn` infers from the first row's value. If it's null/undefined, type detection fails.
217
-
218
- Source: https://github.com/TanStack/table/issues/4711