Krizaka
Documentation

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.

WebBetaData display

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 virtualize for a long list loaded at once (logs, 5,000 events) that must stay smooth.

When not to use

  • For a few figures side by side: stats.Use instead: Stat
  • For a gallery of media where the picture matters more than the columns: a grid of cards.Use instead: Card

Installation

Install

npm install @krizaka/ui @krizaka/tailwind @krizaka/tokens tailwindcss

Styles

@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.

Jobs
Status
Podcast transcriptWhisper largerunning318 s
Catalogue summariesLlama 3.3done96 s
Product shots, autumnFlux 1.1done42 s
Trailer voice-overXTTS v2failed7 s

Cards on a phone

layout="cards": each row a card, the title on top — what auto does in a narrow container.

Your videos
TitleVisibilityViewsTips
Sunset over MontréalPublic12,840$184.00
Behind the scenes, part 2Followers3,120$62.50

Loading

Skeleton rows while the first page loads, the table busy.

Users — Loading
UserRoleJoined

Empty

No record yet: an empty state with the next step.

Invoices
InvoiceAmount

No invoice yet

Your first invoice appears after your first payout.

Error

The request failed: an alert with a retry, in place of the rows.

Audit log
EventAt

The audit log could not be loaded.

5,000 rows

virtualize: only the rows in view are rendered, the header stays.

Events
At
evt-1view2026-10-01 00:00
evt-2tip2026-10-02 01:00
evt-3follow2026-10-03 02:00
evt-4comment2026-10-04 03:00
evt-5view2026-10-05 04:00
evt-6tip2026-10-06 05:00
evt-7follow2026-10-07 06:00
evt-8comment2026-10-08 07:00
evt-9view2026-10-09 08:00
evt-10tip2026-10-10 09:00
evt-11follow2026-10-11 10:00
evt-12comment2026-10-01 11:00
evt-13view2026-10-02 12:00
evt-14tip2026-10-03 13:00

Props

A table of records: sort, selection, loading / empty / error, cards on a phone, windowing for long lists.

PropTypeDefaultDescription
captionrequiredstring—What the table lists ("Jobs", "Your videos"): its <caption>, hidden unless showCaption.
columnsrequiredreadonly DataTableColumn<T>[]—The columns, in order.
getRowIdrequired(row: T) => string—A stable id per row: the React key and the selection's value.
rowsrequiredreadonly T[]—The rows, in the order the server gave (sorted here unless manualSort).
classNamestring—Classes merged on the root.
defaultSelectedreadonly string[][]The ids selected at first, uncontrolled.
defaultSortDataTableSortnullThe sort at first, uncontrolled.
density"comfortable" | "compact"—comfortable (default) · compact.
emptyReactNode—Shown when there is no row (an EmptyState). Default: labels.empty.
errorReactNode—A failure, shown in place of the rows (an Alert, a retry button); announced.
footerReactNode—Under the table: a Pagination, a total.
labelsPartial<DataTableLabels>—The words, passed translated; English by default.
layout"table" | "auto" | "cards"autoauto (default): a table, cards below 36rem of container width. table or cards to force one.
loadingboolean—Rows are loading: skeleton rows (loadingRows of them) and aria-busy.
loadingRowsnumber5How many skeleton rows while loading. Default 5.
manualSortboolean—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).
selectableboolean—Adds a checkbox per row and one in the header.
selectedreadonly string[]—The ids selected (controlled).
showCaptionboolean—Shows the caption above the table.
sortDataTableSort—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 table with a caption (hidden unless showCaption) and scope="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 with aria-pressed.
  • While loading the table is aria-busy and its caption says so; an error is announced (role="alert").
  • Windowed rows carry aria-rowindex and the table aria-rowcount: a screen reader knows where it is in the whole list; the scrolling body is a focusable region.

Keyboard

KeysAction
TabReaches the sort buttons of the headers, the checkboxes and the links of the cells.
SpaceToggles 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 a card: "title" column.
  • Sort on the server (manualSort + onSortChange) when the table shows a page of a larger list.
  • Put a Pagination in footer and a FilterBar above; keep the filters and the page in the address.
  • Say what failed in error with a way to retry; say what to do next in empty (an EmptyState with an action).
  • Align amounts and counts to the end (align: "end") in tabular numbers.

Generated from the code of @krizaka/ui 2.4.0: its meta.ts, its examples and its types.Edit this documentation on GitHub

On this page