Hooks for building lightweight, fast and extendable datagrids for React
—
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.
Enables hierarchical data display with expandable/collapsible rows for nested content.
/**
* Adds row expansion capabilities to the table
* @param hooks - Hook registration object
*/
function useExpanded(hooks: Hooks): void;
interface ExpandedInstance {
/** Toggle expansion state for all rows */
toggleAllRowsExpanded: (expanded?: boolean) => void;
/** Array of currently expanded rows */
expandedRows: Row[];
}
interface ExpandedRowInstance {
/** Whether this row is expanded */
isExpanded: boolean;
/** Whether this row can be expanded */
canExpand: boolean;
/** Sub-rows for this row */
subRows: Row[];
/** Toggle expansion for this row */
toggleRowExpanded: (expanded?: boolean) => void;
/** Get props for expansion toggle element */
getToggleRowExpandedProps: PropGetter;
}Usage Example:
import React from 'react';
import { useTable, useExpanded } from 'react-table';
function ExpandableTable({ columns, data }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
toggleAllRowsExpanded,
state: { expanded },
} = useTable(
{
columns,
data,
getSubRows: (row) => row.subRows || [],
},
useExpanded
);
return (
<div>
<div>
<button onClick={() => toggleAllRowsExpanded()}>
{Object.keys(expanded).length ? 'Collapse All' : 'Expand All'}
</button>
</div>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
<th>Expand</th>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
<td>
{row.canExpand && (
<span
{...row.getToggleRowExpandedProps()}
style={{
cursor: 'pointer',
paddingLeft: `${row.depth * 2}rem`,
}}
>
{row.isExpanded ? '👇' : '👉'}
</span>
)}
</td>
{row.cells.map(cell => (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
);
}Enables single or multi-row selection with customizable selection controls.
/**
* Adds row selection capabilities to the table
* @param hooks - Hook registration object
*/
function useRowSelect(hooks: Hooks): void;
interface RowSelectInstance {
/** Toggle selection for all rows */
toggleAllRowsSelected: (selected?: boolean) => void;
/** Toggle selection for all rows on current page */
toggleAllPageRowsSelected: (selected?: boolean) => void;
/** Get props for select all checkbox */
getToggleAllRowsSelectedProps: PropGetter;
/** Get props for select all page rows checkbox */
getToggleAllPageRowsSelectedProps: PropGetter;
/** Whether all rows are selected */
isAllRowsSelected: boolean;
/** Array of selected row objects */
selectedFlatRows: Row[];
}
interface RowSelectRowInstance {
/** Whether this row is selected */
isSelected: boolean;
/** Whether some child rows are selected (for hierarchical data) */
isSomeSelected: boolean;
/** Toggle selection for this row */
toggleRowSelected: (selected?: boolean) => void;
/** Get props for row selection checkbox */
getToggleRowSelectedProps: PropGetter;
}Usage Example:
import React from 'react';
import { useTable, useRowSelect } from 'react-table';
// Custom checkbox component
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return <input type="checkbox" ref={resolvedRef} {...rest} />;
}
);
function SelectableTable({ columns, data }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
selectedFlatRows,
toggleAllRowsSelected,
getToggleAllRowsSelectedProps,
} = useTable(
{
columns,
data,
},
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Add selection column
{
id: 'selection',
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox
{...row.getToggleRowSelectedProps()}
/>
</div>
),
},
...columns,
]);
}
);
return (
<div>
<div>
<button onClick={() => toggleAllRowsSelected(false)}>
Clear Selection
</button>
<span>Selected: {selectedFlatRows.length} rows</span>
</div>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<tr
{...row.getRowProps()}
className={row.isSelected ? 'selected' : ''}
>
{row.cells.map(cell => (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
))}
</tr>
);
})}
</tbody>
</table>
<div>
<h3>Selected Rows:</h3>
<pre>
{JSON.stringify(
selectedFlatRows.map(row => row.original),
null,
2
)}
</pre>
</div>
</div>
);
}Provides individual state management for each row, useful for per-row configurations or temporary data.
/**
* Adds individual row state management to the table
* @param hooks - Hook registration object
*/
function useRowState(hooks: Hooks): void;
interface RowStateInstance {
/** Individual row state by row ID */
rowState: Record<string, any>;
}
interface RowStateRowInstance {
/** Update state for this specific row */
setState: (updater: any) => void;
/** Current state object for this row */
state: any;
}Usage Example:
import React from 'react';
import { useTable, useRowState } from 'react-table';
function StatefulRowsTable({ columns, data }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable(
{
columns,
data,
},
useRowState
);
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
<th>Actions</th>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
<td>
<button
onClick={() => row.setState({ editing: !row.state.editing })}
>
{row.state.editing ? 'Save' : 'Edit'}
</button>
<button
onClick={() => row.setState({ favorite: !row.state.favorite })}
style={{
color: row.state.favorite ? 'gold' : 'gray'
}}
>
⭐
</button>
</td>
{row.cells.map(cell => (
<td
{...cell.getCellProps()}
style={{
backgroundColor: row.state.editing ? '#f0f0f0' : 'white'
}}
>
{cell.render('Cell')}
</td>
))}
</tr>
);
})}
</tbody>
</table>
);
}Combine multiple row features for advanced table functionality.
import React from 'react';
import {
useTable,
useExpanded,
useRowSelect,
useRowState
} from 'react-table';
function AdvancedRowTable({ columns, data }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
toggleAllRowsExpanded,
selectedFlatRows,
state: { expanded, selectedRowIds },
} = useTable(
{
columns,
data,
getSubRows: (row) => row.children || [],
},
useExpanded,
useRowSelect,
useRowState
);
return (
<div>
<div className="table-controls">
<button onClick={() => toggleAllRowsExpanded()}>
{Object.keys(expanded).length ? 'Collapse All' : 'Expand All'}
</button>
<span>Selected: {selectedFlatRows.length}</span>
</div>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
<th>Select</th>
<th>Expand</th>
<th>Actions</th>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<tr
{...row.getRowProps()}
className={[
row.isSelected && 'selected',
row.state.editing && 'editing',
].filter(Boolean).join(' ')}
>
<td>
<input
type="checkbox"
{...row.getToggleRowSelectedProps()}
/>
</td>
<td>
{row.canExpand && (
<button
{...row.getToggleRowExpandedProps()}
style={{ marginLeft: `${row.depth}rem` }}
>
{row.isExpanded ? '▼' : '▶'}
</button>
)}
</td>
<td>
<button
onClick={() =>
row.setState({ editing: !row.state.editing })
}
>
{row.state.editing ? 'Save' : 'Edit'}
</button>
</td>
{row.cells.map(cell => (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
);
}interface TableState {
/** Expanded row IDs */
expanded: Record<string, boolean>;
/** Selected row IDs */
selectedRowIds: Record<string, boolean>;
/** Individual row states */
rowState: Record<string, any>;
}
interface RowFeatureMeta {
instance: TableInstance;
row: Row;
}Install with Tessl CLI
npx tessl i tessl/npm-react-table