0
# Row Features
1
2
React Table provides comprehensive row-level functionality including expansion, selection, and individual row state management. These features enable building interactive tables with hierarchical data, multi-select capabilities, and per-row customization.
3
4
## Capabilities
5
6
### Row Expansion (useExpanded)
7
8
Enables hierarchical data display with expandable/collapsible rows for nested content.
9
10
```javascript { .api }
11
/**
12
* Adds row expansion capabilities to the table
13
* @param hooks - Hook registration object
14
*/
15
function useExpanded(hooks: Hooks): void;
16
17
interface ExpandedInstance {
18
/** Toggle expansion state for all rows */
19
toggleAllRowsExpanded: (expanded?: boolean) => void;
20
/** Array of currently expanded rows */
21
expandedRows: Row[];
22
}
23
24
interface ExpandedRowInstance {
25
/** Whether this row is expanded */
26
isExpanded: boolean;
27
/** Whether this row can be expanded */
28
canExpand: boolean;
29
/** Sub-rows for this row */
30
subRows: Row[];
31
/** Toggle expansion for this row */
32
toggleRowExpanded: (expanded?: boolean) => void;
33
/** Get props for expansion toggle element */
34
getToggleRowExpandedProps: PropGetter;
35
}
36
```
37
38
**Usage Example:**
39
40
```javascript
41
import React from 'react';
42
import { useTable, useExpanded } from 'react-table';
43
44
function ExpandableTable({ columns, data }) {
45
const {
46
getTableProps,
47
getTableBodyProps,
48
headerGroups,
49
rows,
50
prepareRow,
51
toggleAllRowsExpanded,
52
state: { expanded },
53
} = useTable(
54
{
55
columns,
56
data,
57
getSubRows: (row) => row.subRows || [],
58
},
59
useExpanded
60
);
61
62
return (
63
<div>
64
<div>
65
<button onClick={() => toggleAllRowsExpanded()}>
66
{Object.keys(expanded).length ? 'Collapse All' : 'Expand All'}
67
</button>
68
</div>
69
70
<table {...getTableProps()}>
71
<thead>
72
{headerGroups.map(headerGroup => (
73
<tr {...headerGroup.getHeaderGroupProps()}>
74
<th>Expand</th>
75
{headerGroup.headers.map(column => (
76
<th {...column.getHeaderProps()}>
77
{column.render('Header')}
78
</th>
79
))}
80
</tr>
81
))}
82
</thead>
83
<tbody {...getTableBodyProps()}>
84
{rows.map(row => {
85
prepareRow(row);
86
return (
87
<tr {...row.getRowProps()}>
88
<td>
89
{row.canExpand && (
90
<span
91
{...row.getToggleRowExpandedProps()}
92
style={{
93
cursor: 'pointer',
94
paddingLeft: `${row.depth * 2}rem`,
95
}}
96
>
97
{row.isExpanded ? '👇' : '👉'}
98
</span>
99
)}
100
</td>
101
{row.cells.map(cell => (
102
<td {...cell.getCellProps()}>
103
{cell.render('Cell')}
104
</td>
105
))}
106
</tr>
107
);
108
})}
109
</tbody>
110
</table>
111
</div>
112
);
113
}
114
```
115
116
### Row Selection (useRowSelect)
117
118
Enables single or multi-row selection with customizable selection controls.
119
120
```javascript { .api }
121
/**
122
* Adds row selection capabilities to the table
123
* @param hooks - Hook registration object
124
*/
125
function useRowSelect(hooks: Hooks): void;
126
127
interface RowSelectInstance {
128
/** Toggle selection for all rows */
129
toggleAllRowsSelected: (selected?: boolean) => void;
130
/** Toggle selection for all rows on current page */
131
toggleAllPageRowsSelected: (selected?: boolean) => void;
132
/** Get props for select all checkbox */
133
getToggleAllRowsSelectedProps: PropGetter;
134
/** Get props for select all page rows checkbox */
135
getToggleAllPageRowsSelectedProps: PropGetter;
136
/** Whether all rows are selected */
137
isAllRowsSelected: boolean;
138
/** Array of selected row objects */
139
selectedFlatRows: Row[];
140
}
141
142
interface RowSelectRowInstance {
143
/** Whether this row is selected */
144
isSelected: boolean;
145
/** Whether some child rows are selected (for hierarchical data) */
146
isSomeSelected: boolean;
147
/** Toggle selection for this row */
148
toggleRowSelected: (selected?: boolean) => void;
149
/** Get props for row selection checkbox */
150
getToggleRowSelectedProps: PropGetter;
151
}
152
```
153
154
**Usage Example:**
155
156
```javascript
157
import React from 'react';
158
import { useTable, useRowSelect } from 'react-table';
159
160
// Custom checkbox component
161
const IndeterminateCheckbox = React.forwardRef(
162
({ indeterminate, ...rest }, ref) => {
163
const defaultRef = React.useRef();
164
const resolvedRef = ref || defaultRef;
165
166
React.useEffect(() => {
167
resolvedRef.current.indeterminate = indeterminate;
168
}, [resolvedRef, indeterminate]);
169
170
return <input type="checkbox" ref={resolvedRef} {...rest} />;
171
}
172
);
173
174
function SelectableTable({ columns, data }) {
175
const {
176
getTableProps,
177
getTableBodyProps,
178
headerGroups,
179
rows,
180
prepareRow,
181
selectedFlatRows,
182
toggleAllRowsSelected,
183
getToggleAllRowsSelectedProps,
184
} = useTable(
185
{
186
columns,
187
data,
188
},
189
useRowSelect,
190
(hooks) => {
191
hooks.visibleColumns.push((columns) => [
192
// Add selection column
193
{
194
id: 'selection',
195
Header: ({ getToggleAllRowsSelectedProps }) => (
196
<div>
197
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
198
</div>
199
),
200
Cell: ({ row }) => (
201
<div>
202
<IndeterminateCheckbox
203
{...row.getToggleRowSelectedProps()}
204
/>
205
</div>
206
),
207
},
208
...columns,
209
]);
210
}
211
);
212
213
return (
214
<div>
215
<div>
216
<button onClick={() => toggleAllRowsSelected(false)}>
217
Clear Selection
218
</button>
219
<span>Selected: {selectedFlatRows.length} rows</span>
220
</div>
221
222
<table {...getTableProps()}>
223
<thead>
224
{headerGroups.map(headerGroup => (
225
<tr {...headerGroup.getHeaderGroupProps()}>
226
{headerGroup.headers.map(column => (
227
<th {...column.getHeaderProps()}>
228
{column.render('Header')}
229
</th>
230
))}
231
</tr>
232
))}
233
</thead>
234
<tbody {...getTableBodyProps()}>
235
{rows.map(row => {
236
prepareRow(row);
237
return (
238
<tr
239
{...row.getRowProps()}
240
className={row.isSelected ? 'selected' : ''}
241
>
242
{row.cells.map(cell => (
243
<td {...cell.getCellProps()}>
244
{cell.render('Cell')}
245
</td>
246
))}
247
</tr>
248
);
249
})}
250
</tbody>
251
</table>
252
253
<div>
254
<h3>Selected Rows:</h3>
255
<pre>
256
{JSON.stringify(
257
selectedFlatRows.map(row => row.original),
258
null,
259
2
260
)}
261
</pre>
262
</div>
263
</div>
264
);
265
}
266
```
267
268
### Row State Management (useRowState)
269
270
Provides individual state management for each row, useful for per-row configurations or temporary data.
271
272
```javascript { .api }
273
/**
274
* Adds individual row state management to the table
275
* @param hooks - Hook registration object
276
*/
277
function useRowState(hooks: Hooks): void;
278
279
interface RowStateInstance {
280
/** Individual row state by row ID */
281
rowState: Record<string, any>;
282
}
283
284
interface RowStateRowInstance {
285
/** Update state for this specific row */
286
setState: (updater: any) => void;
287
/** Current state object for this row */
288
state: any;
289
}
290
```
291
292
**Usage Example:**
293
294
```javascript
295
import React from 'react';
296
import { useTable, useRowState } from 'react-table';
297
298
function StatefulRowsTable({ columns, data }) {
299
const {
300
getTableProps,
301
getTableBodyProps,
302
headerGroups,
303
rows,
304
prepareRow,
305
} = useTable(
306
{
307
columns,
308
data,
309
},
310
useRowState
311
);
312
313
return (
314
<table {...getTableProps()}>
315
<thead>
316
{headerGroups.map(headerGroup => (
317
<tr {...headerGroup.getHeaderGroupProps()}>
318
<th>Actions</th>
319
{headerGroup.headers.map(column => (
320
<th {...column.getHeaderProps()}>
321
{column.render('Header')}
322
</th>
323
))}
324
</tr>
325
))}
326
</thead>
327
<tbody {...getTableBodyProps()}>
328
{rows.map(row => {
329
prepareRow(row);
330
return (
331
<tr {...row.getRowProps()}>
332
<td>
333
<button
334
onClick={() => row.setState({ editing: !row.state.editing })}
335
>
336
{row.state.editing ? 'Save' : 'Edit'}
337
</button>
338
<button
339
onClick={() => row.setState({ favorite: !row.state.favorite })}
340
style={{
341
color: row.state.favorite ? 'gold' : 'gray'
342
}}
343
>
344
⭐
345
</button>
346
</td>
347
{row.cells.map(cell => (
348
<td
349
{...cell.getCellProps()}
350
style={{
351
backgroundColor: row.state.editing ? '#f0f0f0' : 'white'
352
}}
353
>
354
{cell.render('Cell')}
355
</td>
356
))}
357
</tr>
358
);
359
})}
360
</tbody>
361
</table>
362
);
363
}
364
```
365
366
### Combined Row Features
367
368
Combine multiple row features for advanced table functionality.
369
370
```javascript
371
import React from 'react';
372
import {
373
useTable,
374
useExpanded,
375
useRowSelect,
376
useRowState
377
} from 'react-table';
378
379
function AdvancedRowTable({ columns, data }) {
380
const {
381
getTableProps,
382
getTableBodyProps,
383
headerGroups,
384
rows,
385
prepareRow,
386
toggleAllRowsExpanded,
387
selectedFlatRows,
388
state: { expanded, selectedRowIds },
389
} = useTable(
390
{
391
columns,
392
data,
393
getSubRows: (row) => row.children || [],
394
},
395
useExpanded,
396
useRowSelect,
397
useRowState
398
);
399
400
return (
401
<div>
402
<div className="table-controls">
403
<button onClick={() => toggleAllRowsExpanded()}>
404
{Object.keys(expanded).length ? 'Collapse All' : 'Expand All'}
405
</button>
406
<span>Selected: {selectedFlatRows.length}</span>
407
</div>
408
409
<table {...getTableProps()}>
410
<thead>
411
{headerGroups.map(headerGroup => (
412
<tr {...headerGroup.getHeaderGroupProps()}>
413
<th>Select</th>
414
<th>Expand</th>
415
<th>Actions</th>
416
{headerGroup.headers.map(column => (
417
<th {...column.getHeaderProps()}>
418
{column.render('Header')}
419
</th>
420
))}
421
</tr>
422
))}
423
</thead>
424
<tbody {...getTableBodyProps()}>
425
{rows.map(row => {
426
prepareRow(row);
427
return (
428
<tr
429
{...row.getRowProps()}
430
className={[
431
row.isSelected && 'selected',
432
row.state.editing && 'editing',
433
].filter(Boolean).join(' ')}
434
>
435
<td>
436
<input
437
type="checkbox"
438
{...row.getToggleRowSelectedProps()}
439
/>
440
</td>
441
<td>
442
{row.canExpand && (
443
<button
444
{...row.getToggleRowExpandedProps()}
445
style={{ marginLeft: `${row.depth}rem` }}
446
>
447
{row.isExpanded ? '▼' : '▶'}
448
</button>
449
)}
450
</td>
451
<td>
452
<button
453
onClick={() =>
454
row.setState({ editing: !row.state.editing })
455
}
456
>
457
{row.state.editing ? 'Save' : 'Edit'}
458
</button>
459
</td>
460
{row.cells.map(cell => (
461
<td {...cell.getCellProps()}>
462
{cell.render('Cell')}
463
</td>
464
))}
465
</tr>
466
);
467
})}
468
</tbody>
469
</table>
470
</div>
471
);
472
}
473
```
474
475
## Types
476
477
```javascript { .api }
478
interface TableState {
479
/** Expanded row IDs */
480
expanded: Record<string, boolean>;
481
/** Selected row IDs */
482
selectedRowIds: Record<string, boolean>;
483
/** Individual row states */
484
rowState: Record<string, any>;
485
}
486
487
interface RowFeatureMeta {
488
instance: TableInstance;
489
row: Row;
490
}
491
```