Data Grid
A feature-rich table with column sorting, per-column filtering, row selection with an indeterminate header checkbox, custom cell rendering, and client-side pagination.
Preview
| Name | Role | Status | Joined | ||
|---|---|---|---|---|---|
| Jamie Rothwell | jamie@example.com | Admin | active | 2024-01-12 | |
| Sam Torres | sam@example.com | Editor | active | 2024-03-08 | |
| Alex Kim | alex@example.com | Viewer | invited | 2025-01-20 | |
| Mia Chen | mia@example.com | Editor | active | 2024-07-30 | |
| Jordan Park | jordan@example.com | Viewer | suspended | 2023-11-02 |
8 results
1 / 2
Column options
| Prop | Type | Description |
|---|---|---|
| key | keyof T | Maps the column to a row property |
| header | string | Column heading text |
| sortable | boolean | Enables click-to-sort with aria-sort |
| filterable | boolean | Shows a filter input above the table |
| align | "left" | "center" | "right" | Text alignment — right for numeric columns |
| width | string | Fixed column width (e.g., '200px') |
| render | (value, row) => ReactNode | Custom cell renderer (badges, links, etc.) |
Keyboard interactions
| Key | Action |
|---|---|
| Tab | Move focus through filter inputs, column headers, checkboxes |
| Space / Enter | Toggle sort on focused column header |
| Space | Toggle row selection on focused checkbox |
Implementation
DataGrid.tsx
tsx
import { DataGrid } from "@/components/ui/DataGrid";
import { Badge } from "@/components/ui/Badge";
const columns = [
{ key: "name", header: "Name", sortable: true, filterable: true },
{ key: "email", header: "Email", sortable: true, filterable: true },
{ key: "role", header: "Role", sortable: true },
{
key: "status",
header: "Status",
sortable: true,
render: (value) => (
<Badge variant={statusVariant[value]}>
{value}
</Badge>
),
},
{ key: "joined", header: "Joined", sortable: true, align: "right" },
];
<DataGrid
columns={columns}
rows={users}
pageSize={10}
selectable
onSelectionChange={(ids) => console.log(ids)}
/>Guidelines
- →Use client-side sorting and filtering for datasets under 500 rows; move to server-side for larger sets.
- →Right-align numeric columns — scanning numbers is easier when they share the same decimal point column.
- →Virtualise rows (react-virtual or TanStack Virtual) when exceeding 200 rows in the DOM.
- →The header checkbox uses the indeterminate state (native ref) when only some rows are selected — never skip this.
- →Avoid more than 8 visible columns — let users customise column visibility for dense data.