# Sorting

> Package `@umriss-ui/table`, version 0.40.0. Demo page: <https://romanhaendler.github.io/umriss-ui/table/sorting/>. Every page in one line: [llms.txt](https://romanhaendler.github.io/umriss-ui/table/llms.txt); every page in full: [llms-full.txt](https://romanhaendler.github.io/umriss-ui/table/llms-full.txt).

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.

```ts
import { useTable } from "@umriss-ui/table";
```

Demo page: https://romanhaendler.github.io/umriss-ui/table/sorting/

## Examples

### Sort by several columns

A click on a header sorts, Shift and click adds a level; `defaultSort` takes the levels the table starts with.

```tsx
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>
  );
}
```

### Sort and export a value that is an object

A value without an order or a text form needs `children`; `sortValue` gives what to sort by, `exportValue` the text for the file.

```tsx
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>
  );
}
```

## Keyboard

| Key | Action |
|---|---|
| `Shift + click` | On a header: adds the column as a further sort level. |

## Props on this page

From `TableProps` — the full table stands on [First table](https://romanhaendler.github.io/umriss-ui/table/first-table/#type-TableProps).

| Prop | Type | Default | Description |
|---|---|---|---|
| [`children`](https://romanhaendler.github.io/umriss-ui/table/first-table/#TableProps-children) | `ReactNode` | — | Columns, `RowDetail`, `RowActions` and the unbound parts. |

#### Accessibility

| Prop | Type | Default | Description |
|---|---|---|---|
| [`ariaLabel`](https://romanhaendler.github.io/umriss-ui/table/first-table/#TableProps-ariaLabel) | `string` | — | Accessible name of the table (not of the outer frame). |

From `TableOptions` — the full table stands on [First table](https://romanhaendler.github.io/umriss-ui/table/first-table/#type-TableOptions).

| Prop | Type | Default | Description |
|---|---|---|---|
| [`rowKey`](https://romanhaendler.github.io/umriss-ui/table/first-table/#TableOptions-rowKey) *required* | `(row: Z) => string` | — | A stable key per row – the basis of selection and expansion. |
| [`defaultSort`](https://romanhaendler.github.io/umriss-ui/table/first-table/#TableOptions-defaultSort) | [`SortLevel`](https://romanhaendler.github.io/umriss-ui/table/first-table/#type-SortLevel)`  \| readonly  `[`SortLevel`](https://romanhaendler.github.io/umriss-ui/table/first-table/#type-SortLevel)`[] \| null` | `null` | The application's sort; it is the default that `view` leaves out. |

From `FieldColumn` — the full table stands on [Column](https://romanhaendler.github.io/umriss-ui/table/column/#type-FieldColumn).

| Prop | Type | Default | Description |
|---|---|---|---|
| [`value`](https://romanhaendler.github.io/umriss-ui/table/column/#FieldColumn-value) *required* | `K` | — | A field of the row. Supplies the column's `id` at the same time. |
| [`format`](https://romanhaendler.github.io/umriss-ui/table/column/#FieldColumn-format) | [`FormatFor`](https://romanhaendler.github.io/umriss-ui/table/column/#type-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`](https://romanhaendler.github.io/umriss-ui/table/column/#FieldColumn-children) | [`Presentation`](https://romanhaendler.github.io/umriss-ui/table/column/#type-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](https://romanhaendler.github.io/umriss-ui/table/column/#type-ColumnBase).

| Prop | Type | Default | Description |
|---|---|---|---|
| [`label`](https://romanhaendler.github.io/umriss-ui/table/column/#ColumnBase-label) *required* | `string` | — | Names the column in its header, in the column menu and in the header row of the export. |
| [`rowHeader`](https://romanhaendler.github.io/umriss-ui/table/column/#ColumnBase-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](https://romanhaendler.github.io/umriss-ui/table/column/#type-ValuePaths).

| Prop | Type | Default | Description |
|---|---|---|---|
| [`sortValue`](https://romanhaendler.github.io/umriss-ui/table/column/#ValuePaths-sortValue) | `(value: `[`Present`](https://romanhaendler.github.io/umriss-ui/table/column/#type-Present)`<W>) => string \| number \| `[`Absent`](https://romanhaendler.github.io/umriss-ui/table/column/#type-Absent) | — | What is sorted by, when not by the value itself. |
| [`exportValue`](https://romanhaendler.github.io/umriss-ui/table/column/#ValuePaths-exportValue) | `(value: `[`Present`](https://romanhaendler.github.io/umriss-ui/table/column/#type-Present)`<W>) => string \| number \| `[`Absent`](https://romanhaendler.github.io/umriss-ui/table/column/#type-Absent) | — | What stands in the export, when not the value itself. |

From `ToolbarProps` — the full table stands on [Toolbar](https://romanhaendler.github.io/umriss-ui/table/toolbar/#type-ToolbarProps).

| Prop | Type | Default | Description |
|---|---|---|---|
| [`children`](https://romanhaendler.github.io/umriss-ui/table/toolbar/#ToolbarProps-children) | `ReactNode` | — | Search, column menu, export – on the left in the table toolbar, before the conditions. |

From `ExportProps` — the full table stands on [Export](https://romanhaendler.github.io/umriss-ui/table/export/#type-ExportProps).

#### Events

| Prop | Type | Default | Description |
|---|---|---|---|
| [`onExport`](https://romanhaendler.github.io/umriss-ui/table/export/#ExportProps-onExport) | `(text: string) => void` | — | Gets the text instead of triggering a file. |

## Known limits

- In [server mode](https://romanhaendler.github.io/umriss-ui/table/server-mode/) the table sorts nothing itself; the sort goes to the server in the request.

What umriss deliberately does not build, and why: [ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/).
