Selection
Built-in row selection, controlled id-based selection, and bulk actions with SelectionToolbar.
DataTable supports two selection modes:
| Mode | Best For | Requirements |
|---|---|---|
| Built-in selection | Local table state with a simple selected field | Rows include selected; pass onDataChange |
| Controlled id selection | Remote data, virtualization, persisted selection | Pass getRowId, selection, and onSelectionChange |
Built-in Selection
Built-in selection is the shortest path when the table owns the row array and each row can carry a selected field.
const [employees, setEmployees] = useState(
sampleEmployees.map((employee) => ({ ...employee, selected: false })),
);
<DataTable
data={employees}
columns={selectionColumns}
enableRowSelection
onDataChange={setEmployees}
/>Controlled Id-based Selection
Use controlled selection when row data comes from a server, a cache, or another store that should not be mutated by the table.
const [employees] = useState(sampleEmployees);
const [selection, setSelection] = useState<RowSelectionState>({});
<DataTable
data={employees}
columns={selectionColumns}
enableRowSelection
getRowId={(employee) => employee.id}
selection={selection}
onSelectionChange={setSelection}
/>Stable row ids matter here. Without getRowId, TanStack falls back to row indexes, which are fragile once sorting, filtering, or virtualization enter the picture.
Bulk Actions
SelectionToolbar is a primitive, but it composes naturally with DataTable selection state for bulk actions.
function SelectableTableWithBulkActions() {
const [employees, setEmployees] = useState(
sampleEmployees.map((employee) => ({ ...employee, selected: false })),
);
const selectedCount = employees.filter((employee) => employee.selected).length;
return (
<div className="relative">
<DataTable
data={employees}
columns={selectionColumns}
enableRowSelection
onDataChange={setEmployees}
/>
<SelectionToolbar
actions={actions}
onClear={() => {
setEmployees((current) =>
current.map((employee) => ({ ...employee, selected: false })),
);
}}
position="container"
selectedCount={selectedCount}
totalCount={employees.length}
/>
</div>
);
}For controlled id-based selection, derive selectedCount from the selection map instead of the row objects.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
enableRowSelection | boolean | false | Add the selection checkbox column |
selection | RowSelectionState | - | Controlled id-based selection state |
onSelectionChange | (selection: RowSelectionState) => void | - | Controlled selection change handler |
onDataChange | (data: TData[]) => void | - | Required for built-in selection mode |
getRowId | (row: TData, index: number) => string | - | Stable ids for controlled selection |
clearSelectionOnOutsideClick | boolean | false | Clear the current selection on outside click |