Shadcn React Table

Search documentation

Search the docs

Row selection

Set enableRowSelection: true to add a leading checkbox column. When rows are selected, a selection alert banner appears showing the current count.

enableRowSelection accepts a boolean or a (row) => boolean predicate (standard TanStack), so you can allow selecting only certain rows.

Stable selection keys

Always provide getRowId so selection keys stay stable across re-renders, sorting, and pagination. Without it, selection is keyed by row index and can drift.

row-selection

Multi-row selection with a select-all checkbox in the header.

Row selectionMulti-row selection with an alert banner.
GitHub
ID
AvaThompsonOwnerEngineeringactive22$45,000Jan 2, 2023
0%
LiamNguyenAdminMarketinginactive25$47,137Jan 13, 2023
9%
NoahSilvaEditorDesignpending28$49,274Jan 24, 2023
18%
EmmaCarterViewerSalesactive31$51,411Feb 4, 2023
27%
OliviaRossiOwnerSupportinactive34$53,548Feb 15, 2023
36%
WilliamWalkerAdminEngineeringpending37$55,685Feb 26, 2023
45%
SophiaPatelEditorMarketingactive40$57,822Mar 9, 2023
54%
JamesMullerViewerDesigninactive43$59,959Mar 20, 2023
63%
IsabellaParkOwnerSalespending46$62,096Mar 31, 2023
72%
LucasReyesAdminSupportactive49$64,233Apr 11, 2023
81%
$4,120,536
Rows per page
1–10 of 48
tsx
const table = useDataTable({
  data,
  columns,
  getRowId: (row) => row.id,
  enableRowSelection: true,
})

return <DataTable table={table} />

Read the current selection at any time. To show it in your own component's render, subscribe to it with stateSelector (the component holding the table does not re-render on table changes by default; see State management):

tsx
const table = useDataTable({
  // …
  enableRowSelection: true,
  stateSelector: (state) => state.rowSelection,
})

const selected = table.getState().rowSelection

Or control it from your own state:

tsx
const table = useDataTable({
  data,
  columns,
  getRowId: (row) => row.id,
  enableRowSelection: true,
  state: { rowSelection },
  onRowSelectionChange: setRowSelection,
})

You can also gate selection per row with a predicate:

tsx
enableRowSelection: (row) => row.original.status !== "archived"

single-selection

For single selection, add enableMultiRowSelection: false. Checking a row unchecks the one before it, and the select-all checkbox goes away.

Single selectionSingle-row selection (no select-all).
GitHub
Select row
ID
AvaThompsonOwnerEngineeringactive22$45,000Jan 2, 2023
0%
LiamNguyenAdminMarketinginactive25$47,137Jan 13, 2023
9%
NoahSilvaEditorDesignpending28$49,274Jan 24, 2023
18%
EmmaCarterViewerSalesactive31$51,411Feb 4, 2023
27%
OliviaRossiOwnerSupportinactive34$53,548Feb 15, 2023
36%
WilliamWalkerAdminEngineeringpending37$55,685Feb 26, 2023
45%
SophiaPatelEditorMarketingactive40$57,822Mar 9, 2023
54%
JamesMullerViewerDesigninactive43$59,959Mar 20, 2023
63%
IsabellaParkOwnerSalespending46$62,096Mar 31, 2023
72%
LucasReyesAdminSupportactive49$64,233Apr 11, 2023
81%
$4,120,536
Rows per page
1–10 of 48
tsx
const table = useDataTable({
  data,
  columns,
  getRowId: (row) => row.id,
  enableRowSelection: true,
  enableMultiRowSelection: false,
})

return <DataTable table={table} />

Selecting a range

Click one row's checkbox, then shift-click another: every row between them takes the same state — selected, or cleared if the shift-clicked row was selected. Ranges follow the rows as displayed (sorted and filtered). Turn it off with enableBatchRowSelection: false.

Bulk actionsDelete the selected rows from a toolbar button.
GitHub
ID
AvaThompsonOwnerEngineeringactive22$45,000Jan 2, 2023
0%
LiamNguyenAdminMarketinginactive25$47,137Jan 13, 2023
9%
NoahSilvaEditorDesignpending28$49,274Jan 24, 2023
18%
EmmaCarterViewerSalesactive31$51,411Feb 4, 2023
27%
OliviaRossiOwnerSupportinactive34$53,548Feb 15, 2023
36%
WilliamWalkerAdminEngineeringpending37$55,685Feb 26, 2023
45%
SophiaPatelEditorMarketingactive40$57,822Mar 9, 2023
54%
JamesMullerViewerDesigninactive43$59,959Mar 20, 2023
63%
IsabellaParkOwnerSalespending46$62,096Mar 31, 2023
72%
LucasReyesAdminSupportactive49$64,233Apr 11, 2023
81%
$1,306,030
Rows per page
1–10 of 20

Select-all behavior

The selection column header carries a select-all checkbox. selectAllMode controls its scope — "page" (default) toggles the current page, "all" toggles every row across pages. Set enableSelectAll: false to hide the header checkbox entirely.

tsx
const table = useDataTable({
  data,
  columns,
  getRowId: (row) => row.id,
  enableRowSelection: true,
  selectAllMode: "all", // "page" | "all"
  enableSelectAll: true,
})

Where the selection banner shows

positionToolbarAlertBanner places the banner with the selected count and Clear selection: "top" (default, between the toolbar and the table), "bottom" (under the table), "head-overlay" or "none".

With "head-overlay" (as in Material React Table) the banner lies over the header row instead, with the select-all checkbox at its start, so selecting rows never pushes the table down. It stays over a sticky header while the table scrolls, spans the visible width when it scrolls sideways, and goes away when nothing is selected, leaving the header clickable again. If you Tab into a header control while it shows, it steps aside until focus leaves the header row. It needs the table head (enableTableHead).

Selection banner over the headerThe selection banner lies over the header row.
GitHub
ID
AvaThompsonOwnerEngineeringactive22$45,000Jan 2, 2023
0%
LiamNguyenAdminMarketinginactive25$47,137Jan 13, 2023
9%
NoahSilvaEditorDesignpending28$49,274Jan 24, 2023
18%
EmmaCarterViewerSalesactive31$51,411Feb 4, 2023
27%
OliviaRossiOwnerSupportinactive34$53,548Feb 15, 2023
36%
WilliamWalkerAdminEngineeringpending37$55,685Feb 26, 2023
45%
SophiaPatelEditorMarketingactive40$57,822Mar 9, 2023
54%
JamesMullerViewerDesigninactive43$59,959Mar 20, 2023
63%
IsabellaParkOwnerSalespending46$62,096Mar 31, 2023
72%
LucasReyesAdminSupportactive49$64,233Apr 11, 2023
81%
$4,120,536
Rows per page
1–10 of 48
tsx
const table = useDataTable({
  data,
  columns,
  getRowId: (row) => row.id,
  enableRowSelection: true,
  positionToolbarAlertBanner: "head-overlay", // "top" | "bottom" | "head-overlay" | "none"
})

Relevant options

PropTypeDefaultDescription
enableSelectAll?booleantrueShow the select-all checkbox in the selection column header. Default true.
selectAllMode?"page" | "all""page"Select-all scope for the header checkbox: the current page ("page", default) or every row ("all").
enableBatchRowSelection?booleantrueShift-click a row's checkbox to select (or clear) every row since the last one clicked. Default true.
positionToolbarAlertBanner?"top" | "bottom" | "head-overlay" | "none""top"Where the row-selection alert banner renders: above the table (`"top"`, default), below it (`"bottom"`), over the header row (`"head-overlay"`, with the select-all checkbox, so selecting rows does not move the table; needs the table head), or nowhere (`"none"`).