One value per row, read from a field or computed. Everything the table does with a column (sort, search, export, total) works on that value, never on what the cell shows.
import { useTable } from "@umriss-ui/table";
Without a presentation the value type decides: text on the left, numbers right-aligned with tabular figures, points in time as date and time, truth values as a word.
A computed value needs an id. One column is the row header: it names the row, cannot be hidden, and the row's checkbox and actions are read out with it.
A computed value (like sortValue, groupValue and rowKey) may be written inline: a new function with the same source text counts as the same one, and nothing is recomputed. A closure that reads an outer variable which has changed reads as unchanged; give the function a new text, or carry the variable in the row data.
value names a field of the row, and the compiler offers only the fields there are; the field name is the column's id as well.
import { useTable } from "@umriss-ui/table";
interface Vehicle {
plate: string;
type: string;
capacity: number;
}
const VEHICLES: Vehicle[] = [
{ plate: "FP 214 K", type: "van", capacity: 1200 },
{ plate: "FP 377 K", type: "e-van", capacity: 900 },
{ plate: "FP 118 R", type: "truck", capacity: 7500 },
];
export default function ValueFromField() {
const { Table, Column } = useTable(VEHICLES, { rowKey: (v) => v.plate });
return (
<Table ariaLabel="Vehicles">
<Column value="plate" label="Plate" rowHeader />
<Column value="type" label="Type" />
<Column value="capacity" label="Payload (kg)" />
</Table>
);
}
When value is a function the column needs an id; the computed value sorts, formats and exports like any other.
import { useTable } from "@umriss-ui/table";
interface Line {
description: string;
quantity: number;
unitPrice: number;
}
const LINES: Line[] = [
{ description: "Desk lamps, LED", quantity: 24, unitPrice: 48.9 },
{ description: "Printer paper, box of 5 reams", quantity: 40, unitPrice: 21.5 },
{ description: "Delivery", quantity: 1, unitPrice: 35 },
];
export default function ComputedValue() {
const { Table, Column } = useTable(LINES, { rowKey: (l) => l.description });
return (
<Table ariaLabel="Invoice lines">
<Column value="description" label="Description" rowHeader />
<Column value="quantity" label="Quantity" />
<Column value="unitPrice" label="Unit price (€)" format={{ decimals: 2 }} />
<Column id="net" label="Net (€)" value={(l) => l.quantity * l.unitPrice} format={{ decimals: 2 }} />
</Table>
);
}
Without children the value decides: text left, numbers right with tabular figures, dates as date and time, booleans as a word.
import { useTable } from "@umriss-ui/table";
interface Service {
name: string;
requests: number;
deployed: Date;
pages: boolean;
openIncidents: number | null;
}
const SERVICES: Service[] = [
{ name: "Checkout", requests: 1840.5, deployed: new Date(2026, 2, 17, 9, 5), pages: true, openIncidents: 1 },
{ name: "Reporting", requests: 18, deployed: new Date(2026, 2, 16, 14, 30), pages: false, openIncidents: null },
];
export default function Defaults() {
const { Table, Column } = useTable(SERVICES, { rowKey: (s) => s.name });
return (
<Table ariaLabel="Defaults per value type">
<Column value="name" label="Text" rowHeader />
<Column value="requests" label="Number" />
<Column value="deployed" label="Point in time" />
<Column value="pages" label="Truth value" />
<Column value="openIncidents" label="Absent" />
</Table>
);
}
rowHeader makes a column the row's name: it cannot be hidden, and the row's checkbox and actions are read out with it.
import { useState } from "react";
import { Stack, Text } from "@umriss-ui/core";
import { ColumnMenu, Toolbar, useTable } from "@umriss-ui/table";
interface Incident {
id: string;
title: string;
}
const INCIDENTS: Incident[] = [
{ id: "INC-1048", title: "Checkout slow, card payments time out" },
{ id: "INC-1047", title: "Webhook deliveries delayed" },
{ id: "INC-1046", title: "Thumbnails missing for new uploads" },
];
export default function RowHeader() {
const [last, setLast] = useState("Nothing opened yet.");
const { Table, Column, RowActions, Action } = useTable(INCIDENTS, { rowKey: (i) => i.id });
return (
<Stack gap={3}>
<Table selectable ariaLabel="Incidents">
<Toolbar>
<ColumnMenu />
</Toolbar>
<Column value="id" label="Incident" rowHeader />
<Column value="title" label="Title" />
<RowActions>
<Action onSelect={(i) => setLast(`${i.id} opened`)}>Open</Action>
</RowActions>
</Table>
<Text size="sm" tone="secondary" role="status">
{last}
</Text>
</Stack>
);
}
The keys of First table, ColumnMenu, RowActions and Checkbox apply here.
The label is the column header's text, and so what a screen reader says for every cell under it. The rowHeader column's cells are row headers: a reader names the row by them, and the row's checkbox and actions are named after them - “Select INC-1048”, “Open: INC-1048”. Without one, each of those says only its own word.
FieldColumn<Z, K extends Field<Z>>| Name | Type | Default | Description |
|---|---|---|---|
valuerequired | K | — | A field of the row. Supplies the column's id at the same time.Shown in: Read a value from a field, Compute a value from the row, Let the value type decide and 118 more |
id | string | — | An id of one's own instead of the field name – for two columns over the same field. Shown in: Pick a standard format (Formats), Share a column between tables (Presets) |
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.Shown in: Compute a value from the row, Set wording and formats once (Provider), Pick a standard format (Formats) and 33 more |
filter | FilterFor<Z[K]> | — | A filter in the column header: "list" offers the values that occur,
"range" two bounds (numbers or points in time), a filter from
columnFilter asks whatever it asks itself. Its condition stands in the
table toolbar.Shown in: A label wider than its column (Width and pinning), Share a column between tables (Presets), Choose from the values that occur (Filter) and 11 more |
group | GroupFor<Z[K]> | — | Groups points in time by "day", "week", "month" or "year".Shown in: Share a column between tables (Presets), Group dates by month (Grouping) |
edit | EditFor<Z[K], Z> | — | Edits the column's cells in place in grid mode: Enter, F2 or typing
starts it, Enter or Tab commits, Escape cancels. The table applies
nothing - the edit is reported through the table's onCellEdit. from EditOptionsShown in: Share a column between tables (Presets), Combine grid mode with groups, pinning and many rows (Grid mode), Edit a comments column (Edits) and 6 more |
editOptions | readonly Present<Z[K]>[] | the values that occur | What edit="select" offers. from EditOptionsShown in: Share a column between tables (Presets), Check an edit before it is reported (Edits), Save a row on purpose (Edits) and 1 more |
validate | (value: Z[K] | null, row: Z) => string | null | undefined | — | Checks a draft before it is reported: a message keeps the editor open
and stands beneath the field, as in a FormField; nothing lets it
through. from EditOptionsShown in: Share a column between tables (Presets), Combine grid mode with groups, pinning and many rows (Grid mode), Check an edit before it is reported (Edits) and 3 more |
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.Shown in: Draw a value with other components (Formats), Show absent values (Formats), Fit what does not fit (Formats) and 9 more |
Also every prop of ColumnBase, ValuePaths and AggregateOptions.
ColumnBase| Name | Type | Default | Description |
|---|---|---|---|
labelrequired | string | — | Names the column in its header, in the column menu and in the header row of the export. Shown in: Read a value from a field, Compute a value from the row, Let the value type decide and 118 more |
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.Shown in: Read a value from a field, Compute a value from the row, Let the value type decide and 117 more |
numeric | boolean | decided by the first value present | Right-aligned with tabular figures. Shown in: Share a column between tables (Presets) |
width | number | the browser's | Initial width in pixels. Shown in: Fit long values (Formats), Fit what does not fit (Formats), Let people set the width (Width and pinning) and 15 more |
resizable | boolean | — | Shows the drag grip on the header cell. Shown in: Let people set the width (Width and pinning), Pin and size from controls of your own (Width and pinning), Share a column between tables (Presets) and 1 more |
pin | Pin"start" | "end" | — | Keeps the column in view while the table scrolls sideways: in a block
before every other column, or after them. The user can change it in the
column menu; the view carries the change. Pinned blocks take at most
half of the width the table shows: on a narrower one the end block, then
the start block scrolls with the rest, and sticks again when there is room. Shown in: Pin columns to both sides (Width and pinning), Share a column between tables (Presets), Combine grid mode with groups, pinning and many rows (Grid mode) and 1 more |
sortable | boolean | true when the value is text, a number, a point in time or a boolean | Whether the column can be sorted by. Shown in: Fit long values (Formats), Fit what does not fit (Formats), Let people set the width (Width and pinning) and 6 more |
searchable | boolean | true for text, false otherwise | Takes part in the search. Shown in: Share a column between tables (Presets), Leave a column out (Search), An uneven organisation (Tree rows) |
groupable | boolean | true when its value is text, a number, a point in time or a boolean, or it has a groupValue | Can the table be grouped by this column? Shown in: Share a column between tables (Presets), Switch grouping off (Grouping) |
ValuePaths<W>| Name | Type | Default | Description |
|---|---|---|---|
sortValue | (value: Present<W>) => string | number | Absent | — | What is sorted by, when not by the value itself. Shown in: Share a column between tables (Presets), Sort and export a value that is an object (Sorting), Work through the alerts (Scenarios) |
exportValue | (value: Present<W>) => string | number | Absent | — | What stands in the export, when not the value itself. Shown in: Share a column between tables (Presets), Sort and export a value that is an object (Sorting) |
groupValue | (value: Present<W>) => string | number | boolean | Date | Absent | — | What is grouped by, when not the value itself - a group header of a number, say.
Rows whose group value is absent form a group of their own. Shown in: Share a column between tables (Presets), Group numbers in bands (Grouping) |
FormatFor<W>What a column accepts as format: a number format for numbers, a date
format for points in time, nothing for every other value.
type FormatFor<W> = [Present<W>] extends [number]
? NumberFormat
: [Present<W>] extends [Date]
? DateFormat
: never;FilterFor<W>What a column accepts as filter: "list" for every value with a text
form, "range" for numbers and points in time, a filter from columnFilter
only where its value type fits.
type FilterFor<W> =
| (IsDisplayable<W> extends true ? "list" : never)
| ([Present<W>] extends [number] ? "range" : [Present<W>] extends [Date] ? "range" : never)
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- the condition belongs to the filter; at the column only the value counts
| ColumnFilter<Present<W>, any>;GroupFor<W>group on a point in time: its rows are grouped by day, ISO week, month or
year.
type GroupFor<W> = [Present<W>] extends [Date] ? DatePeriod : never;EditFor<W, Z>What a column accepts as edit: the core field for its value - "text"
for text, "number" for numbers, "date" for points in time, "select"
for text and numbers - or an editor of one's own.
type EditFor<W, Z> =
| ([Present<W>] extends [string] ? "text" | "select" : never)
| ([Present<W>] extends [number] ? "number" | "select" : never)
| ([Present<W>] extends [Date] ? "date" : never)
| ((editor: CellEditorProps<W, Z>) => ReactNode);Present<W>The value without the absence - that is what children gets.
type Present<W> = Exclude<W, Absent>;Presentation<W, Z>What a column draws in a cell: the value present and its row.
type Presentation<W, Z> = (value: Present<W>, row: Z) => ReactNode;PinWhere a pinned column stands: in the block before or after the others.
type Pin = "start" | "end";AbsentA value that is not there: null or undefined.
type Absent = null | undefined;NumberFormatThe names of the standard presentations of a number.
type NumberFormat = "percent" | "count" | { decimals: number };DateFormatThe names of the standard presentations of a point in time.
type DateFormat = "date" | "time" | "dateTime";IsDisplayable<W>true where the value present can appear as text without children
(Displayable), false otherwise - what decides the filters and
aggregates a column offers.
type IsDisplayable<W> = [Present<W>] extends [never]
? false
: [Present<W>] extends [Displayable]
? true
: false;DatePeriodThe span a point in time is grouped by.
type DatePeriod = "day" | "week" | "month" | "year";CellEditorProps<W, Z>What an editor of one's own receives. The table holds the draft; Enter, Escape and Tab stay the table's, bubbling up from the field.
| Name | Type | Default | Description |
|---|---|---|---|
valuerequired | W | null | — | The draft - the cell's value until the field changes it. |
commitrequired | (value?: W | null) => void | — | Commits - the draft, or the value given. A picker commits on its pick. |
rowrequired | Z | — | |
invalidrequired | boolean | — | The draft did not validate: the field shows it; the message stands beneath. |
labelrequired | string | — | The field's name: what is edited, for which row. The FormField around
the editor already names a core field by it. |
| Name | Type | Default | Description |
|---|---|---|---|
onChangerequired | (value: W | null) => void | — | Changes the draft. |
DisplayableWhat can appear as text without children.
type Displayable = string | number | boolean | Date;