Order the rows by a column, and by further columns where the first ties. Reach for a sort order of your own where the value is an object.
import { useTable } from "@umriss-ui/table";
A click on a header sorts, Shift and click adds a level; defaultSort takes the levels the table starts with.
import { useTable } from "@umriss-ui/table";
interface Incident {
id: string;
title: string;
severity: "SEV1" | "SEV2" | "SEV3";
opened: Date;
assignee: string;
}
const at = (day: number, hours: number, minutes: number) => new Date(2026, 2, day, hours, minutes);
const INCIDENTS: Incident[] = [
{ id: "INC-1048", title: "Checkout slow, card payments time out", severity: "SEV1", opened: at(17, 9, 42), assignee: "Jonas Keller" },
{ id: "INC-1047", title: "Webhook deliveries delayed", severity: "SEV3", opened: at(17, 7, 15), assignee: "Ines Duarte" },
{ id: "INC-1046", title: "Thumbnails missing for new uploads", severity: "SEV2", opened: at(16, 22, 5), assignee: "Tomasz Nowak" },
{ id: "INC-1045", title: "Sign-in codes arrive late", severity: "SEV2", opened: at(16, 14, 20), assignee: "Ada Mwangi" },
{ id: "INC-1043", title: "Search returns no results for some regions", severity: "SEV1", opened: at(14, 18, 30), assignee: "Leila Haddad" },
{ id: "INC-1042", title: "Duplicate reminder e-mails", severity: "SEV3", opened: at(13, 8, 45), assignee: "Sam Okafor" },
];
export default function SortBySeveralColumns() {
const { Table, Column } = useTable(INCIDENTS, {
rowKey: (i) => i.id,
defaultSort: [
{ column: "severity", direction: "asc" },
{ column: "opened", direction: "desc" },
],
});
return (
<Table ariaLabel="Incidents, most severe and newest first">
<Column value="id" label="Incident" rowHeader />
<Column value="title" label="Title" />
<Column value="severity" label="Severity" />
<Column value="opened" label="Opened" format="dateTime" />
<Column value="assignee" label="Assignee" />
</Table>
);
}
A value without an order or a text form needs children; sortValue gives what to sort by, exportValue the text for the file.
import { useState } from "react";
import { Stack, Text } from "@umriss-ui/core";
import { Export, Toolbar, useTable } from "@umriss-ui/table";
interface Parcel {
id: string;
size: { length: number; width: number; height: number };
}
const PARCELS: Parcel[] = [
{ id: "FP-1004210", size: { length: 60, width: 40, height: 40 } },
{ id: "FP-1004223", size: { length: 30, width: 20, height: 10 } },
{ id: "FP-1004236", size: { length: 120, width: 60, height: 20 } },
];
export default function OwnOrdering() {
const [csv, setCsv] = useState("Not exported yet.");
const { Table, Column } = useTable(PARCELS, { rowKey: (p) => p.id });
return (
<Stack gap={3}>
<Table ariaLabel="Parcels">
<Toolbar>
<Export onExport={setCsv} />
</Toolbar>
<Column value="id" label="Parcel" rowHeader />
<Column
value="size"
label="Size (cm)"
sortValue={(s) => s.length * s.width * s.height}
exportValue={(s) => `${s.length} x ${s.width} x ${s.height}`}
>
{(s) => `${s.length} × ${s.width} × ${s.height}`}
</Column>
</Table>
<Text as="pre" size="xs" mono>
{csv}
</Text>
</Stack>
);
}
| Key | Action |
|---|---|
Shift + click |
On a header: adds the column as a further sort level. |
From TableProps — the full table stands on First table.
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Columns, RowDetail, RowActions and the unbound parts. |
| Name | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | — | Accessible name of the table (not of the outer frame). |
From TableOptions — the full table stands on First table.
| Name | Type | Default | Description |
|---|---|---|---|
rowKeyrequired | (row: Z) => string | — | A stable key per row – the basis of selection and expansion. |
defaultSort | | SortLevel | null | The application's sort; it is the default that view leaves out. |
From FieldColumn — the full table stands on Column.
| Name | Type | Default | Description |
|---|---|---|---|
valuerequired | K | — | A field of the row. Supplies the column's id at the same time. |
format | FormatFor<Z[K]> | — | A standard presentation by name: "percent", "count" or
{ decimals } for numbers, "date", "time" or "dateTime" for
points in time. Alignment, sorting, export and footer stay with the value. |
children | Presentation<Z[K], Z> | — | How the value appears: (value, row). Never called for an absent
value. Required where the value has no text form. |
From ColumnBase — the full table stands on Column.
| Name | Type | Default | Description |
|---|---|---|---|
labelrequired | string | — | Names the column in its header, in the column menu and in the header row of the export. |
rowHeader | boolean | — | This column names the row: row header cells, never hideable, the column
stickyRowHeader pins, and the name by which the row's selection, expander and
actions are read out. At most one per table. |
From ValuePaths — the full table stands on Column.
What umriss deliberately does not build, and why: ADR-0032.