Skip to content

Table

Renders tabular data with column definitions, optional sorting, and row selection.

Usage

import { rgb, ui } from "@rezi-ui/core";

ui.table({
  id: "users",
  columns: [
    { key: "name", header: "Name", flex: 1, sortable: true, overflow: "middle" },
    { key: "role", header: "Role", width: 12 },
  ],
  data: state.users,
  getRowKey: (u) => u.id,
  selection: state.selection,
  selectionMode: "multi",
  onSelectionChange: (keys) => app.update((s) => ({ ...s, selection: keys })),
  stripeStyle: { odd: rgb(34, 37, 45) },
  borderStyle: { variant: "double", color: rgb(120, 130, 145) },
})

Boilerplate-free: useTable

Use useTable(ctx, ...) inside composite widgets to auto-manage selection, sorting, and wiring:

import { defineWidget, ui, useTable } from "@rezi-ui/core";

const FilesTable = defineWidget<{ rows: readonly { id: string; name: string; size: number }[] }>(
  (props, ctx) => {
    const table = useTable(ctx, {
      id: "files",
      rows: props.rows,
      columns: [
        { key: "name", header: "Name", flex: 1 },
        { key: "size", header: "Size", width: 10, align: "right" },
      ],
      selectable: "multi",
      sortable: true,
    });

    return ui.table(table.props);
  },
);

useTable returns:

  • props: ready for ui.table(...)
  • selection, clearSelection
  • sortColumn, sortDirection, setSort
  • rows: sorted rows currently rendered by props

Props

Prop Type Default Description
id string required Unique identifier for focus and events
columns TableColumn[] required Column definitions (key, header, width/flex, sortability, renderers, overflow)
data T[] required Row data
getRowKey (row: T, index: number) => string required Stable key for each row
rowHeight number 1 Row height in cells. Use positive values for predictable keyboard and mouse navigation.
headerHeight number 1 Header row height in cells when showHeader is true.
selection string[] [] Currently selected row keys
selectionMode "none" \| "single" \| "multi" "none" Selection behavior
onSelectionChange (keys: string[]) => void - Called when selection changes
sortColumn string - Currently sorted column key
sortDirection "asc" \| "desc" - Current sort direction
onSort (column: string, direction: "asc" \| "desc") => void - Called when sort changes
onRowPress (row: T, index: number) => void - Row activation callback
onRowDoublePress (row: T, index: number) => void - Row double-activation callback (double-click)
virtualized boolean true Enable windowed rendering for large datasets
overscan number 3 Extra rows rendered outside viewport
showHeader boolean true Show/hide header row
stripedRows boolean false Legacy stripe toggle (kept for compatibility)
stripeStyle { odd?, even? } - Stripe background colors. Providing this enables stripes even when stripedRows is false.
border "none" \| "single" "single" Legacy border toggle (kept for compatibility)
borderStyle { variant?, color? } - Border glyph variant (single, double, rounded, heavy, dashed, heavy-dashed) and optional border color
focusConfig FocusConfig - Control focus visuals; { indicator: "none" } suppresses focused row highlight
dsSize "sm" \| "md" \| "lg" "md" Design-system size preset for table recipe spacing
dsTone "default" \| "primary" \| "danger" \| "success" \| "warning" "default" Design-system tone hook for recipe variants

Design System Styling

Tables are design-system styled by default under the active ThemeDefinition. tableRecipe() provides consistent colors for:

import { recipe } from "@rezi-ui/core";
// recipe.table(colors, { state: "header" | "row" | "selectedRow" | "focusedRow" | "stripe" })

This covers header, body rows, selected rows, focused rows, and stripe rows. See the Design System specification for details.

Examples

Sortable table

ui.table({
  id: "files",
  columns: [
    { key: "name", header: "Name", flex: 1, sortable: true },
    { key: "size", header: "Size", width: 10, align: "right", sortable: true },
  ],
  data: files,
  getRowKey: (f) => f.path,
  sortColumn: state.sortColumn,
  sortDirection: state.sortDirection,
  onSort: (column, direction) => app.update((s) => ({ ...s, sortColumn: column, sortDirection: direction })),
})

Behavior

  • Arrow keys navigate rows. Enter activates the selected row.
  • Mouse click on a row selects it and moves focus to the table.
  • In selectionMode: "multi", Ctrl-click toggles the clicked row and Shift-click extends from the last clicked row.
  • Mouse click on a sortable header toggles sort and fires onSort(column, direction).
  • When focused: Up from the first row focuses the header. Left/Right moves between columns. Enter toggles sort on the focused header.
  • Double click on a row fires onRowDoublePress (when provided).
  • Mouse scroll wheel scrolls rows when the table is virtualized.

Notes

  • Tables can be virtualized; prefer virtualization for large datasets.
  • Selection is tracked by row keys. Provide a stable getRowKey.
  • headerHeight is ignored when showHeader is false.
  • Column overflow defaults to "ellipsis" and supports "clip" and "middle" per column.
  • Tables render a single-line frame by default. Set border: "none" to suppress it, and use borderStyle to customize the active frame variant or color.