Components / Data display
SortableDataTable
Compare structured records with sortable columns.
Install
npx shadcn@latest add https://irisui.dev/r/sortable-data-table.jsonimport { SortableDataTable } from "@/components/iris/sortable-data-table/sortable-data-table";Preview
SortableDataTable
A data table with click-to-sort columns, right-aligned numeric columns, optional row selection and rows that glide to their new order.
When to use
- Comparing structured records side by side: projects, listings, contacts.
- When people need to reorder by a column or select several rows for a bulk action.
- Use FilterToolbar above it for filters, Pagination below it for long sets, and Timeline when order is chronological and not user-sortable.
Usage
import { SortableDataTable } from "@/components/iris/sortable-data-table/sortable-data-table";
h(SortableDataTable, {
caption: "Projects",
rows, // [{ id, name, plan, state, storage }]
rowKey: "id",
defaultSort: { key: "storage", direction: "desc" },
selectable: true,
itemName: { one: "project", other: "projects" },
columns: [
{ key: "name", label: "Project" },
{ key: "state", label: "State", render: (v) => h(Badge, { size: "sm" }, v) },
{ key: "storage", label: "Storage", numeric: true, render: (v) => `${v.toFixed(1)} GiB` },
],
});
Props
| Prop | Type | Default | Description |
|---|---|---|---|
rows |
T[] |
— (required) | Records (plain objects). |
columns |
DataColumn<T>[] |
— (required) | { key; label; sortable?; numeric?; width?; render?(value, row) }. Columns sort unless sortable: false. numeric right-aligns with tabular numerals (auto-detected when every value is a number). Without render, values show as text and objects as JSON. |
rowKey |
keyof T | (row) => string |
— (required) | Stable key per row. |
caption |
string |
"Data table" |
Accessible table caption (visually hidden). |
emptyMessage |
string |
"No rows to show" |
Shown when rows is empty. |
defaultSort / onSortChange |
{ key; direction: "asc" | "desc" } / (sort) => void |
— | Initial sort and change callback. Clicking a header toggles asc/desc. |
selectable |
boolean |
false |
Adds a checkbox column, row-click selection and a count line with "Clear selection". |
selectedKeys / defaultSelectedKeys / onSelectionChange |
string[] / string[] / (keys) => void |
— | Controlled or uncontrolled selection. |
itemName |
{ one: string; other: string } |
{ one: "row", other: "rows" } |
Noun for the count line. |
Notes
- Headers are buttons with
aria-sort; the new sort is announced. Arrow keys move between header controls and row checkboxes, Shift-click selects a range, Escape clears the selection. - Columns are measured once and held, so sorting never reflows widths; set
widthto fix a column. - Rows animate to their new position on sort; instant under reduced motion.
- Below 620px the header becomes a scrolling strip of sort controls and each row folds into two lines: the first column with the first numeric column beside it, then the rest.