Data table
A table of records — sort, selection, loading, empty and error states — that turns into cards on a phone and renders only the rows in view when there are thousands.
Web only — React, from @krizaka/ui/data-table. Beta: its API may still change in a minor version.
When to use
- To compare records on the same columns: jobs, videos of a studio, users of an admin, invoices.
- When people sort, select several rows for a bulk action, or scan a status column.
- With
virtualizefor a long list loaded at once (logs, 5,000 events) that must stay smooth.
When not to use
Installation
Install
npm install @krizaka/ui @krizaka/tailwind @krizaka/tokens tailwindcssStyles
@import "tailwindcss";
@import "@krizaka/tailwind";
@import "@krizaka/ui/tailwind.css";Import
import { DataTable } from "@krizaka/ui/data-table";Examples
Jobs
Sortable columns, a status badge, row selection for a bulk action.
| Status | ||||
|---|---|---|---|---|
Cards on a phone
layout="cards": each row a card, the title on top — what auto does in a narrow container.
| Title | Visibility | Views | Tips |
|---|---|---|---|
Loading
Skeleton rows while the first page loads, the table busy.
| User | Role | Joined |
|---|
Empty
No record yet: an empty state with the next step.
| Invoice | Amount |
|---|---|
Error
The request failed: an alert with a retry, in place of the rows.
| Event | At |
|---|---|
5,000 rows
virtualize: only the rows in view are rendered, the header stays.
| At | ||
|---|---|---|
| evt-1 | view | 2026-10-01 00:00 |
| evt-2 | tip | 2026-10-02 01:00 |
| evt-3 | follow | 2026-10-03 02:00 |
| evt-4 | comment | 2026-10-04 03:00 |
| evt-5 | view | 2026-10-05 04:00 |
| evt-6 | tip | 2026-10-06 05:00 |
| evt-7 | follow | 2026-10-07 06:00 |
| evt-8 | comment | 2026-10-08 07:00 |
| evt-9 | view | 2026-10-09 08:00 |
| evt-10 | tip | 2026-10-10 09:00 |
| evt-11 | follow | 2026-10-11 10:00 |
| evt-12 | comment | 2026-10-01 11:00 |
| evt-13 | view | 2026-10-02 12:00 |
| evt-14 | tip | 2026-10-03 13:00 |
Props
A table of records: sort, selection, loading / empty / error, cards on a phone, windowing for long lists.
| Prop | Type | Default | Description |
|---|---|---|---|
captionrequired | string | — | What the table lists ("Jobs", "Your videos"): its <caption>, hidden unless showCaption. |
columnsrequired | readonly DataTableColumn<T>[] | — | The columns, in order. |
getRowIdrequired | (row: T) => string | — | A stable id per row: the React key and the selection's value. |
rowsrequired | readonly T[] | — | The rows, in the order the server gave (sorted here unless manualSort). |
className | string | — | Classes merged on the root. |
defaultSelected | readonly string[] | [] | The ids selected at first, uncontrolled. |
defaultSort | DataTableSort | null | The sort at first, uncontrolled. |
density | "comfortable" | "compact" | — | comfortable (default) · compact. |
empty | ReactNode | — | Shown when there is no row (an EmptyState). Default: labels.empty. |
error | ReactNode | — | A failure, shown in place of the rows (an Alert, a retry button); announced. |
footer | ReactNode | — | Under the table: a Pagination, a total. |
labels | Partial<DataTableLabels> | — | The words, passed translated; English by default. |
layout | "table" | "auto" | "cards" | auto | auto (default): a table, cards below 36rem of container width. table or cards to force one. |
loading | boolean | — | Rows are loading: skeleton rows (loadingRows of them) and aria-busy. |
loadingRows | number | 5 | How many skeleton rows while loading. Default 5. |
manualSort | boolean | — | The server sorts (onSortChange fetches): the rows are shown as given. |
onSelectedChange | ((selected: string[]) => void) | — | Called with the new selection. |
onSortChange | ((sort: DataTableSort) => void) | — | Called when a header asks for a new sort. |
rowLabel | ((row: T) => string) | — | The name of a row in its checkbox's label (default: its id). |
selectable | boolean | — | Adds a checkbox per row and one in the header. |
selected | readonly string[] | — | The ids selected (controlled). |
showCaption | boolean | — | Shows the caption above the table. |
sort | DataTableSort | — | The sorted column (controlled), null for none. |
virtualize | { rowHeight: number; height: number; overscan?: number; } | — | Renders only the rows in view (fixed rowHeight, a scrolling body of height px). Forces the table layout. |
Accessibility
- A
tablewith acaption(hidden unlessshowCaption) andscope="col"headers; the roles are written out, so the cards keep the table's semantics. - The sorted column carries
aria-sort; a sortable header is a button that says its state witharia-pressed. - While loading the table is
aria-busyand its caption says so; an error is announced (role="alert"). - Windowed rows carry
aria-rowindexand the tablearia-rowcount: a screen reader knows where it is in the whole list; the scrolling body is a focusable region.
Keyboard
| Keys | Action |
|---|---|
| Tab | Reaches the sort buttons of the headers, the checkboxes and the links of the cells. |
| Space | Toggles the focused checkbox, or sorts by the focused header. |
Best practices
- Give every column a
header(it labels the value in a card) and the row's name acard: "title"column. - Sort on the server (
manualSort+onSortChange) when the table shows a page of a larger list. - Put a
Paginationinfooterand aFilterBarabove; keep the filters and the page in the address. - Say what failed in
errorwith a way to retry; say what to do next inempty(anEmptyStatewith an action). - Align amounts and counts to the end (
align: "end") in tabular numbers.
Related components
Generated from the code of @krizaka/ui 2.4.0: its meta.ts, its examples and its types.Edit this documentation on GitHub