Data Grid
Sortable, searchable, filterable table with selection, column toggles and pagination, built on TanStack Table.
- 5.4 KB gzipped
- Deps: @tanstack/react-table, lucide-react
- Reduced-motion safe
- Base UI & Radix compatible
Preview
| ORD-7940 | Ana Whitfield | Paid | Card | 2026-10-28 | $12.00 | |
| ORD-7939 | Samuel Okafor | Paid | Bank transfer | 2026-10-28 | $91.00 | |
| ORD-7938 | Priya Raman | Paid | PayPal | 2026-10-27 | $170.00 | |
| ORD-7937 | Jonah Weiss | Pending | Card | 2026-10-27 | $250.00 | |
| ORD-7936 | Lena Kovač | Refunded | Card | 2026-10-26 | $329.00 | |
| ORD-7935 | Felix Lindqvist | Paid | Bank transfer | 2026-10-26 | $408.00 | |
| ORD-7934 | Mei Tanaka | Failed | PayPal | 2026-10-25 | $487.00 | |
| ORD-7933 | Marcus Oyelaran | Pending | Card | 2026-10-25 | $566.00 | |
| ORD-7932 | Ines Moreau | Paid | Card | 2026-10-24 | $646.00 | |
| ORD-7931 | Tomas Berg | Paid | Bank transfer | 2026-10-24 | $725.00 |
1–10 of 48
Installation
pnpm dlx shadcn@latest add https://velora.colorlib.com/r/data-grid.jsonAdds components/velora/data-grid.tsx, installs @tanstack/react-table@^8.21.3, lucide-react.
Examples
Compact, read-only
/api/search | GET | 508 | 2.1 |
/api/orders | POST | 341 | 1.4 |
/api/invoices | GET | 214 | 0.6 |
/api/orders | GET | 182 | 0.2 |
/api/webhooks/stripe | POST | 127 | 0.3 |
1–5 of 7
Props
| Prop | Type | Default |
|---|---|---|
data*Rows to display | TData[] | — |
columns*TanStack Table column definitions; set `meta.label` for a readable name in menus | ColumnDef<TData, TValue>[] | — |
getRowIdStable row id, so selection survives sorting and new data. Defaults to the row index | (row: TData, index: number) => string | — |
searchableShow a search box that matches text in every column | boolean | true |
searchPlaceholder | string | "Search…" |
filtersMulti-select filter buttons, one per column | DataGridFilter[] | [] |
selectableAdd a checkbox column | boolean | false |
onSelectionChangeCalled with the selected rows' data whenever the selection changes | (rows: TData[]) => void | — |
bulkActionsActions shown in a bar while rows are selected | (rows: TData[], clearSelection: () => void) => React.ReactNode | — |
columnToggleShow the column visibility menu | boolean | true |
pageSize | number | 10 |
pageSizesOptions in the rows-per-page menu | number[] | [10, 20, 50] |
defaultSortInitial sort, e.g. `[{ id: "date", desc: true }]` | SortingState | [] |
loadingShow skeleton rows instead of data | boolean | false |
emptyShown when no row matches | React.ReactNode | "No results." |
densityRow height | "compact" | "comfortable" | "comfortable" |
labelAccessible name for the table | string | "Data table" |
className | string | — |
DataGridFilter
interface DataGridFilter {
/** Column id (usually the accessorKey) to filter */
column: string;
/** Label on the filter button */
title: string;
/** Choices; read from the data when omitted */
options?: { label: string; value: string }[];
}Accessibility
- Reduced motion
- Nothing in the table animates. The selection bar fades in only when reduced motion is off.
- Keyboard
- Sortable headers are buttons: Tab reaches them and Enter or Space cycles ascending, descending and unsorted. Search, filter and column menus, row checkboxes and pagination are all native or Radix controls reachable with Tab; menus use arrow keys.
- Screen readers
- A named table whose sortable headers report aria-sort. Every checkbox and icon button has a label, and the result range ("1–10 of 48") is a polite live region, so filtering announces how many rows match.