Search docs

Search blocks and features…

Row Selection & Actions

A checkbox gutter with a tri-state select-all header, shift-click range select, and a configurable bulk-actions dropdown.

Installation

Row selection ships with Data Table — no separate install.

Works with:Data Table
npx shadcn@latest add @kotsas-ui/data-table

This feature confirms its results with toasts. The install adds components/ui/sonner.tsx for you, but the <Toaster /> it exports must be mounted once in your root layout — otherwise the table works but its feedback never appears.

app/layout.tsx
import { Toaster } from "@/components/ui/sonner"

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster />
      </body>
    </html>
  )
}

Usage

Select rows (try shift-click for a range), then open Actions — each action receives the selection via onClick({ rowIds, rows, allMatching }). Selected rows also scope footer aggregation and Delete/Backspace bulk-clear.

1
Set up CI pipeline
High
12
$2,400.00
2
Design onboarding flow
Medium
8
$3,200.00
3
Migrate auth to OAuth2
Urgent
21
$5,600.00
4
Write API docs
Low
0
$900.00
5
Fix pagination bug
High
4
$600.00
6
Upgrade Next.js to 16
Medium
16
$1,800.00

Examples

With server-driven pagination, the select-all header cycles to a third state — every row matching the current filter, loaded or not:

// With server-driven pagination, the select-all header gains a third state:
// none → all loaded → ALL MATCHING (rows not yet loaded included). Your
// actions receive the loaded rows; treat isAllMatchingSelected server-side.
<DataTable
  data={loadedPage}
  columns={columns}
  getRowId={(row) => row.id}
  enableRowSelection
  manualPagination
  totalRowCount={8412}
/>

API Reference

PropTypeDefaultDescription
enableRowSelection?booleanfalseAdds the row-number/checkbox gutter column with tri-state select-all and Sheets/Gmail-style shift-click range select.
actions?DataTableAction[]Bulk actions shown in the Actions dropdown next to Columns. The trigger is disabled while nothing is selected.
DataTableAction.onClick({ rowIds, rows, allMatching }) => voidReceives the selected rows; the popover closes after the click. allMatching is true when the select-all cycle reached "all matching rows" under manual pagination — rows then covers only the loaded subset, so run a server-side bulk path for the full scope.
DataTableAction.icon?ComponentTypeOptional leading icon (any lucide icon works).
DataTableAction.variant?"default" | "destructive""default"Destructive actions render in the destructive color.
DataTableAction.disabled?booleanfalseDisables one action row without hiding it.
getRowId?(row, index) => stringindexStable row identity — required for selection to survive sorting and filtering.