@tanstack/table-core 9.0.0-alpha.47 → 9.0.0-alpha.48
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 +10 -0
- package/dist/core/cells/coreCellsFeature.utils.cjs +11 -8
- package/dist/core/cells/coreCellsFeature.utils.cjs.map +1 -1
- package/dist/core/cells/coreCellsFeature.utils.d.cts +11 -8
- package/dist/core/cells/coreCellsFeature.utils.d.ts +11 -8
- package/dist/core/cells/coreCellsFeature.utils.js +11 -8
- package/dist/core/cells/coreCellsFeature.utils.js.map +1 -1
- package/dist/core/columns/coreColumnsFeature.types.d.cts +7 -6
- package/dist/core/columns/coreColumnsFeature.types.d.ts +7 -6
- package/dist/core/columns/coreColumnsFeature.utils.cjs +37 -27
- package/dist/core/columns/coreColumnsFeature.utils.cjs.map +1 -1
- package/dist/core/columns/coreColumnsFeature.utils.d.cts +37 -27
- package/dist/core/columns/coreColumnsFeature.utils.d.ts +37 -27
- package/dist/core/columns/coreColumnsFeature.utils.js +37 -27
- package/dist/core/columns/coreColumnsFeature.utils.js.map +1 -1
- package/dist/core/headers/coreHeadersFeature.types.d.cts +6 -4
- package/dist/core/headers/coreHeadersFeature.types.d.ts +6 -4
- package/dist/core/headers/coreHeadersFeature.utils.cjs +23 -18
- package/dist/core/headers/coreHeadersFeature.utils.cjs.map +1 -1
- package/dist/core/headers/coreHeadersFeature.utils.d.cts +23 -18
- package/dist/core/headers/coreHeadersFeature.utils.d.ts +23 -18
- package/dist/core/headers/coreHeadersFeature.utils.js +23 -18
- package/dist/core/headers/coreHeadersFeature.utils.js.map +1 -1
- package/dist/core/row-models/coreRowModelsFeature.utils.cjs +48 -36
- package/dist/core/row-models/coreRowModelsFeature.utils.cjs.map +1 -1
- package/dist/core/row-models/coreRowModelsFeature.utils.d.cts +48 -36
- package/dist/core/row-models/coreRowModelsFeature.utils.d.ts +48 -36
- package/dist/core/row-models/coreRowModelsFeature.utils.js +48 -36
- package/dist/core/row-models/coreRowModelsFeature.utils.js.map +1 -1
- package/dist/core/rows/coreRowsFeature.types.d.cts +4 -4
- package/dist/core/rows/coreRowsFeature.types.d.ts +4 -4
- package/dist/core/rows/coreRowsFeature.utils.cjs +34 -28
- package/dist/core/rows/coreRowsFeature.utils.cjs.map +1 -1
- package/dist/core/rows/coreRowsFeature.utils.d.cts +34 -28
- package/dist/core/rows/coreRowsFeature.utils.d.ts +34 -28
- package/dist/core/rows/coreRowsFeature.utils.js +34 -28
- package/dist/core/rows/coreRowsFeature.utils.js.map +1 -1
- package/dist/core/table/coreTablesFeature.types.d.cts +6 -2
- package/dist/core/table/coreTablesFeature.types.d.ts +6 -2
- package/dist/core/table/coreTablesFeature.utils.cjs +2 -2
- package/dist/core/table/coreTablesFeature.utils.cjs.map +1 -1
- package/dist/core/table/coreTablesFeature.utils.d.cts +2 -2
- package/dist/core/table/coreTablesFeature.utils.d.ts +2 -2
- package/dist/core/table/coreTablesFeature.utils.js +2 -2
- package/dist/core/table/coreTablesFeature.utils.js.map +1 -1
- package/dist/features/column-faceting/columnFacetingFeature.types.d.cts +16 -10
- package/dist/features/column-faceting/columnFacetingFeature.types.d.ts +16 -10
- package/dist/features/column-faceting/columnFacetingFeature.utils.cjs +26 -18
- package/dist/features/column-faceting/columnFacetingFeature.utils.cjs.map +1 -1
- package/dist/features/column-faceting/columnFacetingFeature.utils.d.cts +26 -18
- package/dist/features/column-faceting/columnFacetingFeature.utils.d.ts +26 -18
- package/dist/features/column-faceting/columnFacetingFeature.utils.js +26 -18
- package/dist/features/column-faceting/columnFacetingFeature.utils.js.map +1 -1
- package/dist/features/column-filtering/columnFilteringFeature.types.d.cts +21 -11
- package/dist/features/column-filtering/columnFilteringFeature.types.d.ts +21 -11
- package/dist/features/column-filtering/columnFilteringFeature.utils.cjs +39 -28
- package/dist/features/column-filtering/columnFilteringFeature.utils.cjs.map +1 -1
- package/dist/features/column-filtering/columnFilteringFeature.utils.d.cts +39 -28
- package/dist/features/column-filtering/columnFilteringFeature.utils.d.ts +39 -28
- package/dist/features/column-filtering/columnFilteringFeature.utils.js +39 -28
- package/dist/features/column-filtering/columnFilteringFeature.utils.js.map +1 -1
- package/dist/features/column-grouping/columnGroupingFeature.types.d.cts +18 -14
- package/dist/features/column-grouping/columnGroupingFeature.types.d.ts +18 -14
- package/dist/features/column-grouping/columnGroupingFeature.utils.cjs +55 -43
- package/dist/features/column-grouping/columnGroupingFeature.utils.cjs.map +1 -1
- package/dist/features/column-grouping/columnGroupingFeature.utils.d.cts +55 -43
- package/dist/features/column-grouping/columnGroupingFeature.utils.d.ts +55 -43
- package/dist/features/column-grouping/columnGroupingFeature.utils.js +55 -43
- package/dist/features/column-grouping/columnGroupingFeature.utils.js.map +1 -1
- package/dist/features/column-ordering/columnOrderingFeature.types.d.cts +14 -5
- package/dist/features/column-ordering/columnOrderingFeature.types.d.ts +14 -5
- package/dist/features/column-ordering/columnOrderingFeature.utils.cjs +30 -23
- package/dist/features/column-ordering/columnOrderingFeature.utils.cjs.map +1 -1
- package/dist/features/column-ordering/columnOrderingFeature.utils.d.cts +30 -23
- package/dist/features/column-ordering/columnOrderingFeature.utils.d.ts +30 -23
- package/dist/features/column-ordering/columnOrderingFeature.utils.js +30 -23
- package/dist/features/column-ordering/columnOrderingFeature.utils.js.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.types.d.cts +43 -31
- package/dist/features/column-pinning/columnPinningFeature.types.d.ts +43 -31
- package/dist/features/column-pinning/columnPinningFeature.utils.cjs +111 -91
- package/dist/features/column-pinning/columnPinningFeature.utils.cjs.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.utils.d.cts +111 -91
- package/dist/features/column-pinning/columnPinningFeature.utils.d.ts +111 -91
- package/dist/features/column-pinning/columnPinningFeature.utils.js +111 -91
- package/dist/features/column-pinning/columnPinningFeature.utils.js.map +1 -1
- package/dist/features/column-resizing/columnResizingFeature.types.d.cts +9 -9
- package/dist/features/column-resizing/columnResizingFeature.types.d.ts +9 -9
- package/dist/features/column-resizing/columnResizingFeature.utils.cjs +28 -19
- package/dist/features/column-resizing/columnResizingFeature.utils.cjs.map +1 -1
- package/dist/features/column-resizing/columnResizingFeature.utils.d.cts +28 -19
- package/dist/features/column-resizing/columnResizingFeature.utils.d.ts +28 -19
- package/dist/features/column-resizing/columnResizingFeature.utils.js +28 -19
- package/dist/features/column-resizing/columnResizingFeature.utils.js.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.types.d.cts +19 -12
- package/dist/features/column-sizing/columnSizingFeature.types.d.ts +19 -12
- package/dist/features/column-sizing/columnSizingFeature.utils.cjs +52 -41
- package/dist/features/column-sizing/columnSizingFeature.utils.cjs.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.utils.d.cts +52 -41
- package/dist/features/column-sizing/columnSizingFeature.utils.d.ts +52 -41
- package/dist/features/column-sizing/columnSizingFeature.utils.js +52 -41
- package/dist/features/column-sizing/columnSizingFeature.utils.js.map +1 -1
- package/dist/features/column-visibility/columnVisibilityFeature.types.d.cts +16 -13
- package/dist/features/column-visibility/columnVisibilityFeature.types.d.ts +16 -13
- package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs +54 -42
- package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs.map +1 -1
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.cts +54 -42
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.ts +54 -42
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js +54 -42
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js.map +1 -1
- package/dist/features/global-filtering/globalFilteringFeature.types.d.cts +10 -5
- package/dist/features/global-filtering/globalFilteringFeature.types.d.ts +10 -5
- package/dist/features/global-filtering/globalFilteringFeature.utils.cjs +20 -14
- package/dist/features/global-filtering/globalFilteringFeature.utils.cjs.map +1 -1
- package/dist/features/global-filtering/globalFilteringFeature.utils.d.cts +20 -14
- package/dist/features/global-filtering/globalFilteringFeature.utils.d.ts +20 -14
- package/dist/features/global-filtering/globalFilteringFeature.utils.js +20 -14
- package/dist/features/global-filtering/globalFilteringFeature.utils.js.map +1 -1
- package/dist/features/row-expanding/rowExpandingFeature.types.d.cts +16 -15
- package/dist/features/row-expanding/rowExpandingFeature.types.d.ts +16 -15
- package/dist/features/row-expanding/rowExpandingFeature.utils.cjs +56 -41
- package/dist/features/row-expanding/rowExpandingFeature.utils.cjs.map +1 -1
- package/dist/features/row-expanding/rowExpandingFeature.utils.d.cts +56 -41
- package/dist/features/row-expanding/rowExpandingFeature.utils.d.ts +56 -41
- package/dist/features/row-expanding/rowExpandingFeature.utils.js +56 -41
- package/dist/features/row-expanding/rowExpandingFeature.utils.js.map +1 -1
- package/dist/features/row-pagination/rowPaginationFeature.types.d.cts +19 -12
- package/dist/features/row-pagination/rowPaginationFeature.types.d.ts +19 -12
- package/dist/features/row-pagination/rowPaginationFeature.utils.cjs +49 -32
- package/dist/features/row-pagination/rowPaginationFeature.utils.cjs.map +1 -1
- package/dist/features/row-pagination/rowPaginationFeature.utils.d.cts +49 -32
- package/dist/features/row-pagination/rowPaginationFeature.utils.d.ts +49 -32
- package/dist/features/row-pagination/rowPaginationFeature.utils.js +49 -32
- package/dist/features/row-pagination/rowPaginationFeature.utils.js.map +1 -1
- package/dist/features/row-pinning/rowPinningFeature.types.d.cts +12 -8
- package/dist/features/row-pinning/rowPinningFeature.types.d.ts +12 -8
- package/dist/features/row-pinning/rowPinningFeature.utils.cjs +39 -29
- package/dist/features/row-pinning/rowPinningFeature.utils.cjs.map +1 -1
- package/dist/features/row-pinning/rowPinningFeature.utils.d.cts +39 -29
- package/dist/features/row-pinning/rowPinningFeature.utils.d.ts +39 -29
- package/dist/features/row-pinning/rowPinningFeature.utils.js +39 -29
- package/dist/features/row-pinning/rowPinningFeature.utils.js.map +1 -1
- package/dist/features/row-selection/rowSelectionFeature.types.d.cts +30 -24
- package/dist/features/row-selection/rowSelectionFeature.types.d.ts +30 -24
- package/dist/features/row-selection/rowSelectionFeature.utils.cjs +89 -69
- package/dist/features/row-selection/rowSelectionFeature.utils.cjs.map +1 -1
- package/dist/features/row-selection/rowSelectionFeature.utils.d.cts +89 -69
- package/dist/features/row-selection/rowSelectionFeature.utils.d.ts +89 -69
- package/dist/features/row-selection/rowSelectionFeature.utils.js +89 -69
- package/dist/features/row-selection/rowSelectionFeature.utils.js.map +1 -1
- package/dist/features/row-sorting/rowSortingFeature.types.d.cts +10 -8
- package/dist/features/row-sorting/rowSortingFeature.types.d.ts +10 -8
- package/dist/features/row-sorting/rowSortingFeature.utils.cjs +61 -44
- package/dist/features/row-sorting/rowSortingFeature.utils.cjs.map +1 -1
- package/dist/features/row-sorting/rowSortingFeature.utils.d.cts +61 -44
- package/dist/features/row-sorting/rowSortingFeature.utils.d.ts +61 -44
- package/dist/features/row-sorting/rowSortingFeature.utils.js +61 -44
- package/dist/features/row-sorting/rowSortingFeature.utils.js.map +1 -1
- package/dist/fns/aggregationFns.cjs +33 -9
- package/dist/fns/aggregationFns.cjs.map +1 -1
- package/dist/fns/aggregationFns.d.cts +33 -9
- package/dist/fns/aggregationFns.d.ts +33 -9
- package/dist/fns/aggregationFns.js +33 -9
- package/dist/fns/aggregationFns.js.map +1 -1
- package/dist/fns/filterFns.cjs +48 -17
- package/dist/fns/filterFns.cjs.map +1 -1
- package/dist/fns/filterFns.d.cts +42 -15
- package/dist/fns/filterFns.d.ts +42 -15
- package/dist/fns/filterFns.js +48 -17
- package/dist/fns/filterFns.js.map +1 -1
- package/dist/helpers/columnHelper.cjs +9 -4
- package/dist/helpers/columnHelper.cjs.map +1 -1
- package/dist/helpers/columnHelper.d.cts +9 -4
- package/dist/helpers/columnHelper.d.ts +9 -4
- package/dist/helpers/columnHelper.js +9 -4
- package/dist/helpers/columnHelper.js.map +1 -1
- package/dist/helpers/tableOptions.cjs.map +1 -1
- package/dist/helpers/tableOptions.d.cts +12 -9
- package/dist/helpers/tableOptions.d.ts +12 -9
- package/dist/helpers/tableOptions.js.map +1 -1
- package/dist/types/ColumnDef.d.cts +38 -0
- package/dist/types/ColumnDef.d.ts +38 -0
- package/dist/types/TableOptions.d.cts +15 -0
- package/dist/types/TableOptions.d.ts +15 -0
- package/dist/types/TableState.d.cts +12 -0
- package/dist/types/TableState.d.ts +12 -0
- package/package.json +5 -3
- package/skills/column-definitions/SKILL.md +333 -0
- package/skills/column-layout/SKILL.md +328 -0
- package/skills/column-layout/references/subsystems.md +220 -0
- package/skills/customizing-feature-behavior/SKILL.md +416 -0
- package/skills/filtering/SKILL.md +357 -0
- package/skills/filtering/references/faceting-and-fuzzy.md +191 -0
- package/skills/grouping/SKILL.md +448 -0
- package/skills/migrate-v8-to-v9/SKILL.md +495 -0
- package/skills/pagination/SKILL.md +385 -0
- package/skills/row-expanding/SKILL.md +348 -0
- package/skills/row-pinning/SKILL.md +269 -0
- package/skills/row-selection/SKILL.md +391 -0
- package/skills/setup/SKILL.md +400 -0
- package/skills/sorting/SKILL.md +344 -0
- package/skills/state-management/SKILL.md +388 -0
- package/src/core/cells/coreCellsFeature.utils.ts +11 -8
- package/src/core/columns/coreColumnsFeature.types.ts +7 -6
- package/src/core/columns/coreColumnsFeature.utils.ts +37 -27
- package/src/core/headers/coreHeadersFeature.types.ts +6 -4
- package/src/core/headers/coreHeadersFeature.utils.ts +23 -18
- package/src/core/row-models/coreRowModelsFeature.utils.ts +48 -36
- package/src/core/rows/coreRowsFeature.types.ts +4 -4
- package/src/core/rows/coreRowsFeature.utils.ts +34 -28
- package/src/core/table/coreTablesFeature.types.ts +6 -2
- package/src/core/table/coreTablesFeature.utils.ts +2 -2
- package/src/features/column-faceting/columnFacetingFeature.types.ts +16 -10
- package/src/features/column-faceting/columnFacetingFeature.utils.ts +26 -18
- package/src/features/column-filtering/columnFilteringFeature.types.ts +21 -11
- package/src/features/column-filtering/columnFilteringFeature.utils.ts +39 -28
- package/src/features/column-grouping/columnGroupingFeature.types.ts +18 -14
- package/src/features/column-grouping/columnGroupingFeature.utils.ts +55 -43
- package/src/features/column-ordering/columnOrderingFeature.types.ts +14 -5
- package/src/features/column-ordering/columnOrderingFeature.utils.ts +30 -23
- package/src/features/column-pinning/columnPinningFeature.types.ts +43 -31
- package/src/features/column-pinning/columnPinningFeature.utils.ts +111 -91
- package/src/features/column-resizing/columnResizingFeature.types.ts +9 -9
- package/src/features/column-resizing/columnResizingFeature.utils.ts +28 -19
- package/src/features/column-sizing/columnSizingFeature.types.ts +19 -12
- package/src/features/column-sizing/columnSizingFeature.utils.ts +52 -41
- package/src/features/column-visibility/columnVisibilityFeature.types.ts +16 -13
- package/src/features/column-visibility/columnVisibilityFeature.utils.ts +54 -42
- package/src/features/global-filtering/globalFilteringFeature.types.ts +10 -5
- package/src/features/global-filtering/globalFilteringFeature.utils.ts +20 -14
- package/src/features/row-expanding/rowExpandingFeature.types.ts +16 -15
- package/src/features/row-expanding/rowExpandingFeature.utils.ts +56 -41
- package/src/features/row-pagination/rowPaginationFeature.types.ts +19 -12
- package/src/features/row-pagination/rowPaginationFeature.utils.ts +49 -32
- package/src/features/row-pinning/rowPinningFeature.types.ts +12 -8
- package/src/features/row-pinning/rowPinningFeature.utils.ts +39 -29
- package/src/features/row-selection/rowSelectionFeature.types.ts +30 -24
- package/src/features/row-selection/rowSelectionFeature.utils.ts +89 -69
- package/src/features/row-sorting/rowSortingFeature.types.ts +10 -8
- package/src/features/row-sorting/rowSortingFeature.utils.ts +61 -44
- package/src/fns/aggregationFns.ts +33 -9
- package/src/fns/filterFns.ts +48 -17
- package/src/helpers/columnHelper.ts +9 -4
- package/src/helpers/tableOptions.ts +12 -9
- package/src/types/ColumnDef.ts +38 -0
- package/src/types/TableOptions.ts +15 -0
- package/src/types/TableState.ts +12 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: row-expanding
|
|
3
|
+
description: >
|
|
4
|
+
Expand and collapse rows in TanStack Table v9 with the `expandedRowModel`
|
|
5
|
+
stage. Two patterns: (1) tree sub-rows via `getSubRows`, (2) detail panels
|
|
6
|
+
via `getRowCanExpand`. Covers `rowExpandingFeature` + `createExpandedRowModel()`,
|
|
7
|
+
`state.expanded` (ExpandedState = true | Record<string, boolean>),
|
|
8
|
+
`onExpandedChange`, `manualExpanding`, `paginateExpandedRows` (default true),
|
|
9
|
+
`autoResetExpanded`, `row.toggleExpanded` / `getIsExpanded` / `getCanExpand` /
|
|
10
|
+
`getIsAllParentsExpanded` / `getToggleExpandedHandler`, `table.toggleAllRowsExpanded`,
|
|
11
|
+
`row.depth` for indentation, and the `filterFromLeafRows` /
|
|
12
|
+
`maxLeafRowFilterDepth` interaction with filtering.
|
|
13
|
+
type: core
|
|
14
|
+
library: tanstack-table
|
|
15
|
+
library_version: '9.0.0-alpha.47'
|
|
16
|
+
requires:
|
|
17
|
+
- state-management
|
|
18
|
+
sources:
|
|
19
|
+
- TanStack/table:docs/guide/expanding.md
|
|
20
|
+
- TanStack/table:packages/table-core/src/features/row-expanding/rowExpandingFeature.utils.ts
|
|
21
|
+
- TanStack/table:examples/react/expanding/src/main.tsx
|
|
22
|
+
- TanStack/table:examples/react/sub-components/src/main.tsx
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This skill builds on `tanstack-table/state-management`. Read it first for the atom model.
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import {
|
|
31
|
+
tableFeatures,
|
|
32
|
+
rowExpandingFeature,
|
|
33
|
+
createExpandedRowModel,
|
|
34
|
+
constructTable,
|
|
35
|
+
} from '@tanstack/table-core'
|
|
36
|
+
import type { ExpandedState } from '@tanstack/table-core'
|
|
37
|
+
|
|
38
|
+
const _features = tableFeatures({ rowExpandingFeature })
|
|
39
|
+
|
|
40
|
+
// Tree mode — data has nested subRows
|
|
41
|
+
const table = constructTable({
|
|
42
|
+
_features,
|
|
43
|
+
_rowModels: { expandedRowModel: createExpandedRowModel() },
|
|
44
|
+
columns,
|
|
45
|
+
data,
|
|
46
|
+
getSubRows: (row) => row.subRows,
|
|
47
|
+
initialState: { expanded: {} satisfies ExpandedState },
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Or detail-panel mode — every row can expand to a sub-component
|
|
51
|
+
const detailTable = constructTable({
|
|
52
|
+
_features,
|
|
53
|
+
_rowModels: { expandedRowModel: createExpandedRowModel() },
|
|
54
|
+
columns,
|
|
55
|
+
data,
|
|
56
|
+
getRowCanExpand: () => true,
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Core Patterns
|
|
61
|
+
|
|
62
|
+
### Tree table with indentation
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
// From examples/react/expanding/src/main.tsx
|
|
66
|
+
{
|
|
67
|
+
row.getVisibleCells().map((cell, i) => (
|
|
68
|
+
<td key={cell.id}>
|
|
69
|
+
{i === 0 && row.getCanExpand() ? (
|
|
70
|
+
<button
|
|
71
|
+
onClick={row.getToggleExpandedHandler()}
|
|
72
|
+
style={{ paddingLeft: `${row.depth * 2}rem` }}
|
|
73
|
+
>
|
|
74
|
+
{row.getIsExpanded() ? '👇' : '👉'}
|
|
75
|
+
</button>
|
|
76
|
+
) : null}
|
|
77
|
+
<table.FlexRender cell={cell} />
|
|
78
|
+
</td>
|
|
79
|
+
))
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`row.depth` is 0-based. `row.getCanExpand()` returns true when `row.subRows.length > 0`.
|
|
84
|
+
|
|
85
|
+
### Detail panels for flat data
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
// From examples/react/sub-components/src/main.tsx
|
|
89
|
+
{
|
|
90
|
+
table.getRowModel().rows.map((row) => (
|
|
91
|
+
<React.Fragment key={row.id}>
|
|
92
|
+
<tr>
|
|
93
|
+
{row.getVisibleCells().map((cell) => (
|
|
94
|
+
<td key={cell.id}>
|
|
95
|
+
<table.FlexRender cell={cell} />
|
|
96
|
+
</td>
|
|
97
|
+
))}
|
|
98
|
+
</tr>
|
|
99
|
+
{row.getIsExpanded() && (
|
|
100
|
+
<tr>
|
|
101
|
+
<td colSpan={row.getVisibleCells().length}>
|
|
102
|
+
<SubComponent row={row} />
|
|
103
|
+
</td>
|
|
104
|
+
</tr>
|
|
105
|
+
)}
|
|
106
|
+
</React.Fragment>
|
|
107
|
+
))
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Toggle ALL rows expanded at once
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<button onClick={table.getToggleAllRowsExpandedHandler()}>
|
|
115
|
+
{table.getIsAllRowsExpanded() ? '👇' : '👉'} Toggle All
|
|
116
|
+
</button>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Keep expanded children on their parent's page
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const table = constructTable({
|
|
123
|
+
_features: tableFeatures({ rowExpandingFeature, rowPaginationFeature }),
|
|
124
|
+
_rowModels: {
|
|
125
|
+
expandedRowModel: createExpandedRowModel(),
|
|
126
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
127
|
+
},
|
|
128
|
+
columns,
|
|
129
|
+
data,
|
|
130
|
+
getSubRows: (r) => r.subRows,
|
|
131
|
+
paginateExpandedRows: false, // children stay glued to their parent
|
|
132
|
+
})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`paginateExpandedRows: true` (default) flows expanded children through pagination — each child counts toward `pageSize`. `false` keeps them stuck under their parent.
|
|
136
|
+
|
|
137
|
+
### Tree filtering with leaf-match propagation
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const table = constructTable({
|
|
141
|
+
_features: tableFeatures({ rowExpandingFeature, columnFilteringFeature }),
|
|
142
|
+
_rowModels: {
|
|
143
|
+
expandedRowModel: createExpandedRowModel(),
|
|
144
|
+
filteredRowModel: createFilteredRowModel(filterFns),
|
|
145
|
+
},
|
|
146
|
+
columns,
|
|
147
|
+
data,
|
|
148
|
+
getSubRows: (r) => r.subRows,
|
|
149
|
+
filterFromLeafRows: true, // keep parent visible when ANY descendant matches
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Common Mistakes
|
|
154
|
+
|
|
155
|
+
### [HIGH] Setting `getRowCanExpand: () => true` together with tree data
|
|
156
|
+
|
|
157
|
+
Wrong:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
// every row gets an expander icon, including leaves with no subRows
|
|
161
|
+
const table = useTable({
|
|
162
|
+
getRowCanExpand: () => true,
|
|
163
|
+
getSubRows: (r) => r.subRows,
|
|
164
|
+
})
|
|
165
|
+
// In cell: row.getCanExpand() always true → leaf rows show 👉 with nothing to expand
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Correct:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
// For pure tree data, omit getRowCanExpand and let it auto-detect:
|
|
172
|
+
const table = useTable({
|
|
173
|
+
_features,
|
|
174
|
+
_rowModels: { expandedRowModel: createExpandedRowModel() },
|
|
175
|
+
columns,
|
|
176
|
+
data,
|
|
177
|
+
getSubRows: (row) => row.subRows,
|
|
178
|
+
// row.getCanExpand() is true only when subRows.length > 0
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// For pure detail panels, override and skip getSubRows:
|
|
182
|
+
const table = useTable({
|
|
183
|
+
_features: tableFeatures({ rowExpandingFeature }),
|
|
184
|
+
_rowModels: { expandedRowModel: createExpandedRowModel() },
|
|
185
|
+
columns,
|
|
186
|
+
data,
|
|
187
|
+
getRowCanExpand: () => true,
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`row_getCanExpand` resolves to `options.getRowCanExpand?.(row) ?? (enableExpanding ?? true) && !!row.subRows.length`. When `getRowCanExpand` is set, it wins — including for leaves.
|
|
192
|
+
|
|
193
|
+
Source: packages/table-core/src/features/row-expanding/rowExpandingFeature.utils.ts
|
|
194
|
+
|
|
195
|
+
### [MEDIUM] Setting `paginateExpandedRows: false` and expecting `pageSize` to be a hard cap
|
|
196
|
+
|
|
197
|
+
Wrong:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
const table = useTable({
|
|
201
|
+
paginateExpandedRows: false,
|
|
202
|
+
initialState: { pagination: { pageSize: 10 } },
|
|
203
|
+
// user expands all parents → 10 parents * 5 children = 60 visible rows
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Correct:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
// Default behavior — children flow through pagination, pageSize is enforced:
|
|
211
|
+
const table = useTable({
|
|
212
|
+
_features,
|
|
213
|
+
_rowModels: {
|
|
214
|
+
expandedRowModel: createExpandedRowModel(),
|
|
215
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
216
|
+
},
|
|
217
|
+
columns,
|
|
218
|
+
data,
|
|
219
|
+
getSubRows: (r) => r.subRows,
|
|
220
|
+
// paginateExpandedRows defaults to true
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// OR keep children with parent — accept that pageSize is a soft cap:
|
|
224
|
+
// paginateExpandedRows: false
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`paginateExpandedRows: false` inflates each parent's page slice via `expandRows` — more rows render than `pageSize`. Pick deliberately.
|
|
228
|
+
|
|
229
|
+
Source: packages/table-core/src/features/row-pagination/createPaginatedRowModel.ts
|
|
230
|
+
|
|
231
|
+
### [MEDIUM] Storing `expanded` as `true` then writing into it like a Record
|
|
232
|
+
|
|
233
|
+
Wrong:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
// spreading `true` (a boolean) into an object gives {} - all rows collapse except [id]
|
|
237
|
+
setExpanded((old) => ({ ...old, [row.id]: true }))
|
|
238
|
+
// When old === true, this becomes { [row.id]: true } — everything else collapses!
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Correct:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
// Prefer the built-in handler — it materializes properly:
|
|
245
|
+
<button onClick={row.getToggleExpandedHandler()}>
|
|
246
|
+
{row.getIsExpanded() ? '👇' : '👉'}
|
|
247
|
+
</button>
|
|
248
|
+
|
|
249
|
+
// Or handle materialization yourself:
|
|
250
|
+
table.setExpanded((old) => {
|
|
251
|
+
if (old === true) {
|
|
252
|
+
const map: Record<string, boolean> = {}
|
|
253
|
+
Object.keys(table.getRowModel().rowsById).forEach((id) => { map[id] = true })
|
|
254
|
+
return { ...map, [row.id]: !map[row.id] }
|
|
255
|
+
}
|
|
256
|
+
return { ...old, [row.id]: !(old as Record<string, boolean>)[row.id] }
|
|
257
|
+
})
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
`ExpandedState = true | Record<string, boolean>`. The `true` literal means "all rows expanded" — `row.toggleExpanded` materializes it correctly before applying per-row changes.
|
|
261
|
+
|
|
262
|
+
Source: packages/table-core/src/features/row-expanding/rowExpandingFeature.utils.ts
|
|
263
|
+
|
|
264
|
+
### [MEDIUM] `manualExpanding: true` with `expandedRowModel` registered
|
|
265
|
+
|
|
266
|
+
Wrong:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
// manualExpanding bypasses the expanded row model; sub-rows are never flattened
|
|
270
|
+
const table = useTable({
|
|
271
|
+
_features: tableFeatures({ rowExpandingFeature }),
|
|
272
|
+
_rowModels: { expandedRowModel: createExpandedRowModel() }, // ignored
|
|
273
|
+
columns,
|
|
274
|
+
data,
|
|
275
|
+
getSubRows: (r) => r.subRows,
|
|
276
|
+
manualExpanding: true,
|
|
277
|
+
})
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Correct:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
// Manual expanding is for server-side patterns where the server returns
|
|
284
|
+
// a pre-flattened view based on which rows are expanded.
|
|
285
|
+
const table = useTable({
|
|
286
|
+
_features: tableFeatures({ rowExpandingFeature }),
|
|
287
|
+
_rowModels: {}, // no expandedRowModel for manual mode
|
|
288
|
+
columns,
|
|
289
|
+
data: dataQuery.data, // server returns flattened rows when expanded
|
|
290
|
+
manualExpanding: true,
|
|
291
|
+
state: { expanded },
|
|
292
|
+
onExpandedChange: setExpanded,
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
// For client-side tree, omit manualExpanding:
|
|
296
|
+
const clientTable = useTable({
|
|
297
|
+
_features: tableFeatures({ rowExpandingFeature }),
|
|
298
|
+
_rowModels: { expandedRowModel: createExpandedRowModel() },
|
|
299
|
+
columns,
|
|
300
|
+
data,
|
|
301
|
+
getSubRows: (r) => r.subRows,
|
|
302
|
+
})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
With `manualExpanding: true`, `getExpandedRowModel` skips the registered factory and returns `getPreExpandedRowModel()` (sorted rows). The expanded state still tracks "which rows are open" but the row model is NOT inflated.
|
|
306
|
+
|
|
307
|
+
Source: packages/table-core/src/core/row-models/coreRowModelsFeature.utils.ts
|
|
308
|
+
|
|
309
|
+
### [CRITICAL] Reimplementing tree flattening manually
|
|
310
|
+
|
|
311
|
+
Wrong:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
// Hand-rolled tree-to-flat conversion
|
|
315
|
+
const flatRows = useMemo(() => {
|
|
316
|
+
const out: Person[] = []
|
|
317
|
+
function walk(rows: Person[], depth = 0) {
|
|
318
|
+
rows.forEach((r) => {
|
|
319
|
+
out.push({ ...r, depth })
|
|
320
|
+
if (expanded[r.id]) walk(r.subRows ?? [], depth + 1)
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
walk(data)
|
|
324
|
+
return out
|
|
325
|
+
}, [data, expanded])
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Correct:
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
const table = useTable({
|
|
332
|
+
_features: tableFeatures({ rowExpandingFeature }),
|
|
333
|
+
_rowModels: { expandedRowModel: createExpandedRowModel() },
|
|
334
|
+
columns, data,
|
|
335
|
+
getSubRows: (r) => r.subRows,
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// then: table.getRowModel().rows already has row.depth and the expanded view
|
|
339
|
+
table.getRowModel().rows.map((row) => /* render with row.depth */)
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Source: maintainer interview (Phase 4, 2026-05-17)
|
|
343
|
+
|
|
344
|
+
## See also
|
|
345
|
+
|
|
346
|
+
- `tanstack-table/grouping` — pairs with expanding for drill-down on grouped rows
|
|
347
|
+
- `tanstack-table/pagination` — `paginateExpandedRows` interaction
|
|
348
|
+
- `tanstack-table/filtering` — `filterFromLeafRows` / `maxLeafRowFilterDepth` for tree filtering
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: row-pinning
|
|
3
|
+
description: >
|
|
4
|
+
Pin specific rows to a top or bottom region in TanStack Table v9. State shape
|
|
5
|
+
is `rowPinning: { top: string[]; bottom: string[] }` keyed by `row.id`. Covers
|
|
6
|
+
`rowPinningFeature`, `row.pin(position, includeLeafRows?, includeParentRows?)`,
|
|
7
|
+
`row.getIsPinned` / `getPinnedIndex` / `getCanPin`, `table.getTopRows` /
|
|
8
|
+
`getBottomRows` / `getCenterRows` / `getIsSomeRowsPinned`, the
|
|
9
|
+
`enableRowPinning` option (bool or row predicate), and `keepPinnedRows`
|
|
10
|
+
(default true — persist across pagination/filtering vs. hide when filtered out).
|
|
11
|
+
Simpler pipeline than column pinning — only one reorder step: Row Pinning →
|
|
12
|
+
Sorting.
|
|
13
|
+
type: core
|
|
14
|
+
library: tanstack-table
|
|
15
|
+
library_version: '9.0.0-alpha.47'
|
|
16
|
+
requires:
|
|
17
|
+
- state-management
|
|
18
|
+
sources:
|
|
19
|
+
- TanStack/table:docs/guide/row-pinning.md
|
|
20
|
+
- TanStack/table:packages/table-core/src/features/row-pinning/rowPinningFeature.utils.ts
|
|
21
|
+
- TanStack/table:examples/react/row-pinning/src/main.tsx
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
This skill builds on `tanstack-table/state-management`. Read it first for the atom model.
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import {
|
|
30
|
+
tableFeatures,
|
|
31
|
+
rowPinningFeature,
|
|
32
|
+
rowPaginationFeature,
|
|
33
|
+
createPaginatedRowModel,
|
|
34
|
+
constructTable,
|
|
35
|
+
} from '@tanstack/table-core'
|
|
36
|
+
import type { RowPinningState } from '@tanstack/table-core'
|
|
37
|
+
|
|
38
|
+
const _features = tableFeatures({ rowPinningFeature, rowPaginationFeature })
|
|
39
|
+
|
|
40
|
+
const table = constructTable({
|
|
41
|
+
_features,
|
|
42
|
+
_rowModels: {
|
|
43
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
44
|
+
},
|
|
45
|
+
columns,
|
|
46
|
+
data,
|
|
47
|
+
getRowId: (row) => row.userId, // ← essentially mandatory
|
|
48
|
+
initialState: {
|
|
49
|
+
rowPinning: { top: [], bottom: [] } satisfies RowPinningState,
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Pin a row
|
|
54
|
+
row.pin('top') // or 'bottom' | false
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Core Patterns
|
|
58
|
+
|
|
59
|
+
### Pin/unpin buttons in a cell
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
// From examples/react/row-pinning/src/main.tsx
|
|
63
|
+
<button onClick={() => row.pin('top')} disabled={!row.getCanPin()}>📌⬆</button>
|
|
64
|
+
<button onClick={() => row.pin('bottom')} disabled={!row.getCanPin()}>📌⬇</button>
|
|
65
|
+
{row.getIsPinned() && <button onClick={() => row.pin(false)}>✖ Unpin</button>}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For grouped/expanded data, pass include flags:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
row.pin('top', /* includeLeafRows */ true, /* includeParentRows */ false)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Render pinned rows separately
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<tbody>
|
|
78
|
+
{table.getTopRows().map((row) => (
|
|
79
|
+
<PinnedRow key={row.id} row={row} table={table} />
|
|
80
|
+
))}
|
|
81
|
+
{table.getCenterRows().map((row) => (
|
|
82
|
+
<tr key={row.id}>
|
|
83
|
+
{row.getAllCells().map((cell) => (
|
|
84
|
+
<td key={cell.id}>
|
|
85
|
+
<table.FlexRender cell={cell} />
|
|
86
|
+
</td>
|
|
87
|
+
))}
|
|
88
|
+
</tr>
|
|
89
|
+
))}
|
|
90
|
+
{table.getBottomRows().map((row) => (
|
|
91
|
+
<PinnedRow key={row.id} row={row} table={table} />
|
|
92
|
+
))}
|
|
93
|
+
</tbody>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Disable persistence across pagination
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const table = constructTable({
|
|
100
|
+
_features: tableFeatures({ rowPinningFeature, columnFilteringFeature }),
|
|
101
|
+
_rowModels: { filteredRowModel: createFilteredRowModel(filterFns) },
|
|
102
|
+
columns,
|
|
103
|
+
data,
|
|
104
|
+
getRowId: (row) => row.id,
|
|
105
|
+
keepPinnedRows: false, // pinned rows disappear when filtered/paginated out
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`keepPinnedRows: true` (default) keeps pinned rows visible even when their underlying row would otherwise be filtered or paginated away.
|
|
110
|
+
|
|
111
|
+
### Conditional pin permission
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const table = constructTable({
|
|
115
|
+
_features,
|
|
116
|
+
columns,
|
|
117
|
+
data,
|
|
118
|
+
enableRowPinning: (row) => !row.original.archived, // predicate form
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Common Mistakes
|
|
123
|
+
|
|
124
|
+
### [HIGH] Omitting `getRowId` so pins attach to array indices
|
|
125
|
+
|
|
126
|
+
Wrong:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
// row.id defaults to row.index; pin survives wrong rows after refetch
|
|
130
|
+
const table = useTable({
|
|
131
|
+
_features: tableFeatures({ rowPinningFeature, rowPaginationFeature }),
|
|
132
|
+
data, // refetched periodically
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Correct:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
const table = useTable({
|
|
140
|
+
_features: tableFeatures({ rowPinningFeature, rowPaginationFeature }),
|
|
141
|
+
data,
|
|
142
|
+
getRowId: (row) => row.userId, // or row.uuid, row.id from API, etc.
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// For grouped/expanded data, pass the include flags too:
|
|
146
|
+
row.pin('top', includeLeafRows, includeParentRows)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`rowPinning.top` and `rowPinning.bottom` are arrays of string row ids. Default `row.id` is the data array index — refetched data reuses index 3 for a different record, but the pinning state still pins index 3.
|
|
150
|
+
|
|
151
|
+
Source: docs/guide/row-selection.md (same root principle); examples/react/row-pinning/src/main.tsx
|
|
152
|
+
|
|
153
|
+
### [MEDIUM] Surprise behavior from `keepPinnedRows: true` default
|
|
154
|
+
|
|
155
|
+
Wrong:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
// Expecting pinned rows to vanish on filter, but they don't (default)
|
|
159
|
+
const table = useTable({
|
|
160
|
+
_features: tableFeatures({ rowPinningFeature, columnFilteringFeature }),
|
|
161
|
+
// keepPinnedRows defaults to true; pinned rows survive filtering
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Correct:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
// Be explicit about the UX you want
|
|
169
|
+
const table = useTable({
|
|
170
|
+
_features: tableFeatures({ rowPinningFeature, columnFilteringFeature }),
|
|
171
|
+
keepPinnedRows: false, // pinned rows disappear when filtered/paginated out
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Or keep the default and render pinned separately:
|
|
175
|
+
<tbody>
|
|
176
|
+
{table.getTopRows().map((row) => <PinnedRow row={row} key={row.id} />)}
|
|
177
|
+
{table.getCenterRows().map((row) => <Row row={row} key={row.id} />)}
|
|
178
|
+
{table.getBottomRows().map((row) => <PinnedRow row={row} key={row.id} />)}
|
|
179
|
+
</tbody>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`keepPinnedRows: true` makes `getTopRows()` / `getBottomRows()` search the full pre-pagination row set; `false` only finds rows currently in the row model.
|
|
183
|
+
|
|
184
|
+
Source: packages/table-core/src/features/row-pinning/rowPinningFeature.utils.ts; examples/react/row-pinning/src/main.tsx
|
|
185
|
+
|
|
186
|
+
### [MEDIUM] Rendering pinned rows TWICE (once at top/bottom, once in main flow)
|
|
187
|
+
|
|
188
|
+
Wrong:
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
<tbody>
|
|
192
|
+
{table.getTopRows().map((row) => (
|
|
193
|
+
<PinnedRow row={row} key={row.id} />
|
|
194
|
+
))}
|
|
195
|
+
{table.getRowModel().rows.map(
|
|
196
|
+
(
|
|
197
|
+
row, // ← still includes pinned rows
|
|
198
|
+
) => (
|
|
199
|
+
<tr key={row.id}>...</tr>
|
|
200
|
+
),
|
|
201
|
+
)}
|
|
202
|
+
{table.getBottomRows().map((row) => (
|
|
203
|
+
<PinnedRow row={row} key={row.id} />
|
|
204
|
+
))}
|
|
205
|
+
</tbody>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Correct:
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
<tbody>
|
|
212
|
+
{table.getTopRows().map((row) => (
|
|
213
|
+
<PinnedRow key={row.id} row={row} table={table} />
|
|
214
|
+
))}
|
|
215
|
+
{table.getCenterRows().map((row) => (
|
|
216
|
+
<tr key={row.id}>
|
|
217
|
+
{row.getAllCells().map((cell) => (
|
|
218
|
+
<td key={cell.id}>
|
|
219
|
+
<table.FlexRender cell={cell} />
|
|
220
|
+
</td>
|
|
221
|
+
))}
|
|
222
|
+
</tr>
|
|
223
|
+
))}
|
|
224
|
+
{table.getBottomRows().map((row) => (
|
|
225
|
+
<PinnedRow key={row.id} row={row} table={table} />
|
|
226
|
+
))}
|
|
227
|
+
</tbody>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`getRowModel()` returns the complete current row model with pinned rows still in it. Use `getCenterRows()` for the main flow. Use `getRowModel()` only if you intentionally want pinned rows duplicated.
|
|
231
|
+
|
|
232
|
+
Source: examples/react/row-pinning/src/main.tsx
|
|
233
|
+
|
|
234
|
+
### [CRITICAL] Reimplementing pin behavior manually
|
|
235
|
+
|
|
236
|
+
Wrong:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
// Hand-rolled "pinned" map + manual filter on render
|
|
240
|
+
const [pinned, setPinned] = useState<Record<string, true>>({})
|
|
241
|
+
const pinnedRows = rows.filter((r) => pinned[r.id])
|
|
242
|
+
const otherRows = rows.filter((r) => !pinned[r.id])
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Correct:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
const table = useTable({
|
|
249
|
+
_features: tableFeatures({ rowPinningFeature }),
|
|
250
|
+
_rowModels: {},
|
|
251
|
+
columns,
|
|
252
|
+
data,
|
|
253
|
+
getRowId: (row) => row.id,
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
row.pin('top') // pin one row
|
|
257
|
+
table.setRowPinning({ top: ['a', 'b'], bottom: [] }) // bulk set
|
|
258
|
+
table.getTopRows()
|
|
259
|
+
table.getCenterRows()
|
|
260
|
+
table.getBottomRows()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Source: maintainer interview (Phase 4, 2026-05-17)
|
|
264
|
+
|
|
265
|
+
## See also
|
|
266
|
+
|
|
267
|
+
- `tanstack-table/state-management` — `rowPinning` state slice ownership
|
|
268
|
+
- `tanstack-table/row-selection` — same `getRowId` stability concern
|
|
269
|
+
- `tanstack-table/column-layout` — column pinning sits in a separate, more complex pipeline
|