Skip to main content

DataTable

Interactive

See DataTable in Storybook for every variant, with live controls.

A dense table with typed columns, optional sorting and clickable rows. Generic over the row type.

import { DataTable, type Column } from "@kairosis/eidos";

interface Run {
id: string;
service: string;
duration: string;
}

const columns: Column<Run>[] = [
{ key: "id", label: "Run", mono: true, sortable: true, width: 90 },
{ key: "service", label: "Service", sortable: true },
{ key: "duration", label: "Duration", mono: true, align: "right" },
];

<DataTable columns={columns} rows={runs} rowKey="id" onRowClick={open} />;

Props

PropTypeDefaultDescription
columnsColumn<T>[]Required
rowsT[]Required
rowKeykeyof Trow indexField to key rows by
onRowClick(row: T) => voidMakes rows hoverable and clickable
sortKeystringCurrently sorted column
sortDir"asc" | "desc"
onSort(key: string) => void

Column<T> is { key, label, align?, width?, mono?, sortable?, render? }. render(row) replaces the cell's content.

Guidelines

  • Sorting is controlled. onSort reports intent — it tells you which header was clicked. You reorder rows and pass back sortKey/sortDir. DataTable never sorts your data, so it can't disagree with the server.
  • Set mono on any column holding a data value — IDs, timestamps, durations, counts. It also switches on tabular figures so digits align down the column.
  • Use render for cells that aren't plain text (a Badge for status).
  • Pass rowKey, or rows fall back to index keys and will re-key on reorder.
  • Cells truncate at 320px rather than wrapping, keeping row heights uniform.