Data grid

Composable table for sorting, filtering, paginating, and selecting rows.

#100000Avery S.avery.s1@churchmediasquad.comYouthWestBetaVolunteerPaused2024-08-152026-04-10
7
208
$3,151
#100001Riley T.riley.t2@churchmediasquad.comWorshipOnlineGammaStaffArchived2024-09-012026-04-23
8
149
$1,801
#100002Cameron P.cameron.p3@churchmediasquad.comHospitalityNorthDeltaDirectorActive2024-10-142026-04-09
9
90
$452
#100003Drew K.drew.k4@churchmediasquad.comOutreachSouthAlphaLeadPaused2024-11-272026-04-22
10
31
$5,102
#100004Sam M.sam.m5@churchmediasquad.comKidsEastBetaCoordinatorArchived2024-12-132026-04-08
11
212
$3,753
#100005Taylor L.taylor.l6@churchmediasquad.comCommunicationsWestGammaVolunteerActive2024-01-262026-04-21
12
153
$2,403
#100006Morgan B.morgan.b7@churchmediasquad.comProductionOnlineDeltaStaffPaused2024-02-122026-04-07
13
94
$1,054
#100007Casey R.casey.r8@churchmediasquad.comYouthNorthAlphaDirectorArchived2024-03-252026-04-20
14
35
$3,064
#100008Reese S.reese.s9@churchmediasquad.comWorshipSouthBetaLeadActive2024-04-112026-04-06
15
216
$1,715
#100009Quinn T.quinn.t10@churchmediasquad.comHospitalityEastGammaCoordinatorPaused2024-05-242026-04-19
16
157
$365
#100010Hayden P.hayden.p11@churchmediasquad.comHospitalityWestDeltaVolunteerArchived2024-06-102026-04-05
17
98
$5,016
#100011Jordan K.jordan.k12@churchmediasquad.comOutreachOnlineAlphaStaffActive2024-07-232026-04-18
18
39
$3,666
#100012Avery M.avery.m13@churchmediasquad.comKidsNorthBetaDirectorPaused2024-08-092026-04-04
19
220
$2,317
#100013Riley L.riley.l14@churchmediasquad.comCommunicationsSouthGammaLeadArchived2024-09-222026-04-17
0
161
$967
#100014Cameron B.cameron.b15@churchmediasquad.comProductionEastDeltaCoordinatorActive2024-10-082026-04-03
1
102
$5,618
60 total row(s).
Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/data-grid.json
Usage
Pass `columns` and `data` directly to `<DataGrid>` — the component wires up sorting, filtering, pagination, and row selection through props. The data grid depends on `@tanstack/react-table` — install it alongside the master file.
$Terminal
npm install @tanstack/react-table
TSImport
import { DataGrid, type ColumnDef } from "@/components/ui/data-grid";
TSExample
import { DataGrid, type ColumnDef } from "@/components/ui/data-grid";

type Project = {
  id: string;
  name: string;
  status: "Active" | "Paused" | "Archived";
};

const PROJECTS: Project[] = [
  { id: "p_1", name: "Sermon series", status: "Active" },
  { id: "p_2", name: "Mid-week graphics", status: "Active" },
  { id: "p_3", name: "Volunteer recruitment", status: "Paused" },
];

const columns: ColumnDef<Project, unknown>[] = [
  { accessorKey: "name", header: "Project", meta: { label: "Project" } },
  { accessorKey: "status", header: "Status", meta: { label: "Status" } },
];

export function Example() {
  return (
    <DataGrid
      columns={columns}
      data={PROJECTS}
      searchable="Filter projects..."
      getRowId={(row) => row.id}
    />
  );
}
Composition
Anatomy of the DataGrid primitive.
// Flat one-liner — toolbar / table / pagination composed automatically
<DataGrid
  columns={columns}
  data={data}
  searchable="Filter rows..."   // false | true | placeholder string
  selectable                    // prepends the checkbox column
  hidableColumns                // adds the View menu
  paginated={{ pageSize: 10 }}  // true | { pageSize, pageSizes }
  scrollHeight="420px"
  stickyHeader
  getRowId={(row) => row.id}
/>

// Slot escape hatch — use when you need external table state access
// (e.g. reading rowSelection to drive a bulk-action toolbar)
import { DataGridRoot, useDataGrid, DataGridTable, DataGridToolbar } from "@/components/ui/data-grid";

const table = useDataGrid({ data, columns, features: { selection: true }, getRowId });
const selectedIds = Object.keys(table.getState().rowSelection ?? {});

<DataGridRoot table={table}>
  {selectedIds.length > 0 && (
    <DataGridToolbar>
      <span>{selectedIds.length} selected</span>
    </DataGridToolbar>
  )}
  <DataGridTable />
</DataGridRoot>

// All slot exports are retained as the escape hatch:
// DataGridRoot, DataGridToolbar, DataGridSearch, DataGridColumnVisibility,
// DataGridTable, DataGridPagination, DataGridSelectColumn, useDataGrid
Plain rows
Every feature off — just rows + columns rendered into the styled `<Table>`. Drop `<DataGridTable>` inside `<DataGrid>` and you're done.
ProjectOwnerStatusSpendUpdated
Sermon series — EasterJordan B.Active
$4,320
2026-04-21
Mid-week graphicsAvery K.Active
$1,180
2026-04-20
Volunteer recruitmentRiley S.Paused
$0
2026-04-15
Outreach videoCameron L.Active
$12,400
2026-04-19
Annual reportDrew P.Archived
$8,750
2026-03-30
Kids check-in flowJordan B.Active
$2,100
2026-04-18
Stewardship campaignAvery K.Active
$6,220
2026-04-17
Worship night promoSam R.Active
$3,410
2026-04-16
Hospitality refreshRiley S.Paused
$540
2026-04-12
Online giving redesignCameron L.Active
$9,800
2026-04-11
Event landing pagesDrew P.Archived
$2,900
2026-03-26
Worship slides — MaySam R.Active
$700
2026-04-10
Sorting (with sticky header & scroll)
Wide 60×13 grid with the full feature stack: sortable + filterable headers, sticky `stickyHeader` while scrolling, hidable columns, row selection, and 15-row pagination. Scrollbars are hidden — drag/wheel scroll horizontally and vertically inside the rounded card.
#100000Avery S.avery.s1@churchmediasquad.comYouthWestBetaVolunteerPaused2024-08-152026-04-10
7
208
$3,151
#100001Riley T.riley.t2@churchmediasquad.comWorshipOnlineGammaStaffArchived2024-09-012026-04-23
8
149
$1,801
#100002Cameron P.cameron.p3@churchmediasquad.comHospitalityNorthDeltaDirectorActive2024-10-142026-04-09
9
90
$452
#100003Drew K.drew.k4@churchmediasquad.comOutreachSouthAlphaLeadPaused2024-11-272026-04-22
10
31
$5,102
#100004Sam M.sam.m5@churchmediasquad.comKidsEastBetaCoordinatorArchived2024-12-132026-04-08
11
212
$3,753
#100005Taylor L.taylor.l6@churchmediasquad.comCommunicationsWestGammaVolunteerActive2024-01-262026-04-21
12
153
$2,403
#100006Morgan B.morgan.b7@churchmediasquad.comProductionOnlineDeltaStaffPaused2024-02-122026-04-07
13
94
$1,054
#100007Casey R.casey.r8@churchmediasquad.comYouthNorthAlphaDirectorArchived2024-03-252026-04-20
14
35
$3,064
#100008Reese S.reese.s9@churchmediasquad.comWorshipSouthBetaLeadActive2024-04-112026-04-06
15
216
$1,715
#100009Quinn T.quinn.t10@churchmediasquad.comHospitalityEastGammaCoordinatorPaused2024-05-242026-04-19
16
157
$365
#100010Hayden P.hayden.p11@churchmediasquad.comHospitalityWestDeltaVolunteerArchived2024-06-102026-04-05
17
98
$5,016
#100011Jordan K.jordan.k12@churchmediasquad.comOutreachOnlineAlphaStaffActive2024-07-232026-04-18
18
39
$3,666
#100012Avery M.avery.m13@churchmediasquad.comKidsNorthBetaDirectorPaused2024-08-092026-04-04
19
220
$2,317
#100013Riley L.riley.l14@churchmediasquad.comCommunicationsSouthGammaLeadArchived2024-09-222026-04-17
0
161
$967
#100014Cameron B.cameron.b15@churchmediasquad.comProductionEastDeltaCoordinatorActive2024-10-082026-04-03
1
102
$5,618
60 total row(s).
Global filter
Pair `features: { filtering: true }` with `<DataGridSearch>` inside the toolbar. Bound to TanStack's `globalFilter` so every visible column gets searched.
Sermon series — EasterJordan B.Active
$4,320
2026-04-21
Mid-week graphicsAvery K.Active
$1,180
2026-04-20
Volunteer recruitmentRiley S.Paused
$0
2026-04-15
Outreach videoCameron L.Active
$12,400
2026-04-19
Annual reportDrew P.Archived
$8,750
2026-03-30
Kids check-in flowJordan B.Active
$2,100
2026-04-18
Stewardship campaignAvery K.Active
$6,220
2026-04-17
Worship night promoSam R.Active
$3,410
2026-04-16
Hospitality refreshRiley S.Paused
$540
2026-04-12
Online giving redesignCameron L.Active
$9,800
2026-04-11
Event landing pagesDrew P.Archived
$2,900
2026-03-26
Worship slides — MaySam R.Active
$700
2026-04-10
Advanced filter (query builder)
Set `advancedFilter` and pass `renderAdvancedFilter={(table) => <DataGridAdvancedFilter table={table} />}` (install the `data-grid-advanced-filter` item) — the menu auto-populates from your `columns` + `data`: it infers each column's variant (text / number / date / select) and derives select options from the row values, no `meta.filter` required. Users stack typed rules — text, number (+ between), select, multi-select, date (+ between), boolean — combine them with an editable **AND / OR** join, nest **filter groups**, drag to reorder, and the search box keeps working alongside. Add `meta.filter` only to override (force `multiSelect`, supply custom options, etc.). Filtering is evaluated as a boolean tree over TanStack's global filter.
Sermon series — EasterJordan B.Active
$4,320
2026-04-21
Mid-week graphicsAvery K.Active
$1,180
2026-04-20
Volunteer recruitmentRiley S.Paused
$0
2026-04-15
Outreach videoCameron L.Active
$12,400
2026-04-19
Annual reportDrew P.Archived
$8,750
2026-03-30
Kids check-in flowJordan B.Active
$2,100
2026-04-18
12 total row(s).
Row actions
Add a non-sorting, non-hidable `actions` column whose `cell` renders a squad `DropdownMenu` (popover-style menu) behind a ghost `…` button — copy / view / edit, plus a `variant="destructive"` delete. Each item's `onClick` runs your handler (here, a toast).
Actions
Sermon series — EasterJordan B.Active
$4,320
2026-04-21
Mid-week graphicsAvery K.Active
$1,180
2026-04-20
Volunteer recruitmentRiley S.Paused
$0
2026-04-15
Outreach videoCameron L.Active
$12,400
2026-04-19
Annual reportDrew P.Archived
$8,750
2026-03-30
Kids check-in flowJordan B.Active
$2,100
2026-04-18
12 total row(s).
Editable cells
Set `editable` and give each editable column a `meta.editable` ({ variant, options }). Click a cell to edit — `text`/`number`/`url` show a type-constrained input (commit on Enter/blur, Esc cancels); `select` opens a squad `DropdownMenu`; `badge` shows a colored `Badge` + a searchable single-select combobox; `date` opens an inline Calendar + time popover; `avatars` shows an `AvatarGroup` and opens a searchable multi-select combobox. The grid is uncontrolled: handle `onCellEdit(rowId, columnId, value)` and update your `data`.
Row selection
Prepend `DataGridSelectColumn` to your columns array, set `features: { selection: true }`, and pass a stable `getRowId`. Read `table.getState().rowSelection` for the selected ids.
Sermon series — EasterJordan B.Active
$4,320
2026-04-21
Mid-week graphicsAvery K.Active
$1,180
2026-04-20
Volunteer recruitmentRiley S.Paused
$0
2026-04-15
Outreach videoCameron L.Active
$12,400
2026-04-19
Annual reportDrew P.Archived
$8,750
2026-03-30
Kids check-in flowJordan B.Active
$2,100
2026-04-18
Stewardship campaignAvery K.Active
$6,220
2026-04-17
Worship night promoSam R.Active
$3,410
2026-04-16
Hospitality refreshRiley S.Paused
$540
2026-04-12
Online giving redesignCameron L.Active
$9,800
2026-04-11
Event landing pagesDrew P.Archived
$2,900
2026-03-26
Worship slides — MaySam R.Active
$700
2026-04-10
Floating bulk-action toolbar
Pass `bulkActions` to `<DataGrid>` and the grid renders a floating `<Toolbar>` from its own selection state — it slides up from the bottom-center of the viewport whenever one or more rows are checked. The callback receives `{ selectedIds, clearSelection }`; return the toolbar `items` (label, Archive / Delete actions, a trailing ✕ wired to `clearSelection`). Requires `selectable`; override the anchor with `bulkActionsPosition`.
Sermon series — EasterJordan B.Active
$4,320
2026-04-21
Mid-week graphicsAvery K.Active
$1,180
2026-04-20
Volunteer recruitmentRiley S.Paused
$0
2026-04-15
Outreach videoCameron L.Active
$12,400
2026-04-19
Annual reportDrew P.Archived
$8,750
2026-03-30
Kids check-in flowJordan B.Active
$2,100
2026-04-18
Stewardship campaignAvery K.Active
$6,220
2026-04-17
Worship night promoSam R.Active
$3,410
2026-04-16
Hospitality refreshRiley S.Paused
$540
2026-04-12
Online giving redesignCameron L.Active
$9,800
2026-04-11
Event landing pagesDrew P.Archived
$2,900
2026-03-26
Worship slides — MaySam R.Active
$700
2026-04-10
Column visibility
`features: { columnVisibility: true }` + `<DataGridColumnVisibility>` lets users hide columns. Set `enableHiding: false` on a column def to keep it pinned.
Hide columns to focus on what matters.
Sermon series — EasterJordan B.Active
$4,320
2026-04-21
Mid-week graphicsAvery K.Active
$1,180
2026-04-20
Volunteer recruitmentRiley S.Paused
$0
2026-04-15
Outreach videoCameron L.Active
$12,400
2026-04-19
Annual reportDrew P.Archived
$8,750
2026-03-30
Kids check-in flowJordan B.Active
$2,100
2026-04-18
Stewardship campaignAvery K.Active
$6,220
2026-04-17
Worship night promoSam R.Active
$3,410
2026-04-16
Hospitality refreshRiley S.Paused
$540
2026-04-12
Online giving redesignCameron L.Active
$9,800
2026-04-11
Event landing pagesDrew P.Archived
$2,900
2026-03-26
Worship slides — MaySam R.Active
$700
2026-04-10
Card section wrapper
Wrap the grid in a `<Card>` with a `<SectionHeader>` on top for a dashboard-style panel: bold title, optional inline action ('View all →'), and a hairline-free table flush against the card edges. Toggle `font="heading"` (Albra display face) or `font="sans"` (Inter) on the SectionHeader to match the surrounding page voice.

Active projects

Sermon series — EasterJordan B.Active
$4,320
2026-04-21
Mid-week graphicsAvery K.Active
$1,180
2026-04-20
Volunteer recruitmentRiley S.Paused
$0
2026-04-15
Outreach videoCameron L.Active
$12,400
2026-04-19
Annual reportDrew P.Archived
$8,750
2026-03-30
12 total row(s).
Everything together
The full stack on one grid: global search, the rule-based **advanced filter**, sortable headers, row selection with a **floating bulk-action toolbar**, column visibility, pagination, **inline editable cells** (text/number/url/select/badge/date/avatars), and a per-row **actions menu**. The advanced filter auto-infers its fields from the data — no `meta.filter` needed.
Actions
5 total row(s).
API Reference
Props for the flat `<DataGrid>` component. For the compound escape-hatch API, see the slot subcomponent props below.

DataGrid

PropTypeDefaultDescription
dataTData[]Row data array.
columnsColumnDef<TData, unknown>[]TanStack column definitions. Use `meta.label` for human-readable names.
searchableboolean | stringfalseRender the global search input. Pass a string to use it as the placeholder.
advancedFilterbooleanfalseEnable the rule-based advanced filter logic (typed rules + AND/OR + nested groups). Render the menu by also passing `renderAdvancedFilter`. Coexists with `searchable`.
renderAdvancedFilter(table) => ReactNodeRender the advanced-filter menu in the toolbar — typically `(table) => <DataGridAdvancedFilter table={table} />`. The menu ships as the separate `data-grid-advanced-filter` item, so the core grid stays dependency-free until you opt in. Only used with `advancedFilter`.
editablebooleanfalseEnable inline cell editing. Columns opt in via `meta.editable` ({ variant, options }). The grid is uncontrolled — handle `onCellEdit` and update your own `data`.
onCellEdit(rowId, columnId, value) => voidFires when an editable cell commits. `value` is the parsed value (numbers for `number` cells).
meta.editable.variant"text" | "number" | "url" | "select" | "badge" | "date" | "avatars"Per-column (on `ColumnDef.meta.editable`) — the editor + accepted input. `number` accepts numbers only; `url` types as a URL; `select` opens a `DropdownMenu` of `options`; `badge` renders a colored `Badge` and edits via a searchable single-select combobox (each option carries a `color`); `date` opens an inline Calendar + time popover (commits an ISO string); `avatars` shows an `AvatarGroup` and opens a searchable multi-select combobox (commits the selected values as `string[]`).
meta.editable.options{ label; value }[]Choices for a `select` editable cell.
sortablebooleantrueEnable/disable column sorting.
paginatedboolean | { pageSize?: number; pageSizes?: number[] }falseEnable pagination. Pass `true` for defaults (pageSize 10, pageSizes [5,10,20,50]) or an object to override.
selectablebooleanfalsePrepend the checkbox select column and enable row selection.
onSelectionChange(selectedIds: string[]) => voidFires when the row-selection set changes. Receives the selected row ids.
bulkActions(ctx: { selectedIds: string[]; clearSelection: () => void }) => ToolbarItem[]Render a floating bulk-action `<Toolbar>` driven by the grid's own selection state — it slides in whenever rows are selected. Return the toolbar `items`; the callback gets the selected ids plus a `clearSelection` helper. Requires `selectable`.
bulkActionsPosition"bottom-center" | "top-center" | "bottom-right" | "bottom-left""bottom-center"Where the `bulkActions` toolbar anchors.
hidableColumnsbooleanfalseRender the column-visibility (View) menu in the toolbar.
scrollHeightstring | numberConstrain the table to a CSS max-height and add overflow-auto.
stickyHeaderbooleanPin `<thead>` to the top of the scroll container.
getRowId(row: TData, index: number) => stringStable row id — required for selection survival across sorting/filtering.
pageSizenumber10Shorthand page size when `paginated` is `true`. Ignored when `paginated` is an object.
emptyStateReactNodeRendered in the empty-state cell when no rows match.
toolbarSlotReactNodeExtra content rendered right-aligned in the toolbar.
cardboolean | { title?: string; titleFont?: "heading" | "sans"; titleColor?: "default" | "violet"; description?: string; action?: ReactNode; fullHeight?: boolean }falseWrap the grid in a dashboard `<Card>` with an optional SectionHeader (title + right-aligned action). When set, the inner table border is stripped so the table flows into the card edges. Defaults: `titleColor="violet"`, `titleFont="heading"`, `fullHeight=true`.
classNamestringExtra classes on the outermost wrapper div.

useDataGrid (compound escape hatch)

PropTypeDefaultDescription
dataTData[]Row data array.
columnsColumnDef<TData, unknown>[]TanStack column definitions. Use `meta.label` for human-readable names.
features{ sorting?, filtering?, pagination?, selection?, columnVisibility? }all true except selectionToggle the row models that are wired in. Disabled features render no-ops.
pageSizenumber10Initial page size when pagination is enabled.
getRowId(row: TData, index: number) => stringStable row id — required for selection survival across re-fetches.

DataGridAdvancedFilter

PropTypeDefaultDescription
tableTableInstanceThe TanStack table from `useDataGrid({ advancedFilter: true })`. The flat `<DataGrid advancedFilter />` wires this for you.
labelstring"Filter"Trigger button label.
disabledbooleanDisable the trigger.
meta.filter.variant"text" | "number" | "date" | "select" | "multiSelect" | "boolean"Optional override (on `ColumnDef.meta.filter`). When omitted, the variant is inferred from the column's data (numbers → number, dates → date, low-cardinality strings → select, else text). Every filterable column is offered in the builder.
meta.filter.options{ label; value; icon?; count? }[]Optional choices for `select` / `multiSelect`. When omitted, options are auto-derived from the distinct values in the row data.

DataGridTable

PropTypeDefaultDescription
stickyHeaderbooleanPin <thead> while the body scrolls.
scrollHeightstring | numberConstrain the wrapper height and add overflow-auto on both axes.
classNamestringExtra classes on the table wrapper.

DataGridToolbar

PropTypeDefaultDescription
childrenReactNodeAnything you want in the toolbar — search, filters, bulk actions.

DataGridPagination

PropTypeDefaultDescription
pageSizesnumber[][5, 10, 20, 50]Selectable page sizes.