Data table
Many records in a sortable, selectable, virtualised grid.
Guidance
Data tables are the default way to show many records: schemas, members, audit logs, assets.
The header band is mm-sunken with 12px sentence-case mm-text-2 labels and stays put while
you scroll. Rows are separated by hairlines, never zebra stripes; hover uses mm-sunken and
selected rows mm-selected, where tertiary text steps up to mm-text-2. Numbers are
right-aligned and tabular with units in the header; dates are relative, in the text face with
tabular figures; only types and IDs use mono cells. Links in cells are ink, underlined on hover
and focus. Status cells show a pill only when something needs attention: succeeded and
allowed are a plain quiet word. Rows follow
--mm-row (40px, or 36px under data-density="compact" or density="compact"): reduce row
height, never font size.
Rows are virtualised, so 100,000 records scroll smoothly while the markup stays an ARIA grid
with aria-rowcount, aria-rowindex, aria-colcount and aria-colindex.
Keyboard (https://www.w3.org/WAI/ARIA/apg/patterns/grid/): the grid is one tab stop. Arrow keys
move between cells, Home and End go to the start and end of the row, Ctrl+Home and Ctrl+End to
the first and last cell, Page Up and Page Down move a screen at a time. On a header, Enter or
Space sorts, Alt+Shift+Left and Right move the column (reorderable), and F2 moves to the
resize handle, where Left and Right resize (Shift for bigger steps) and Escape returns. In the
body, Space toggles the row's selection (Shift+Space selects the range from the last toggled
row, as Shift+click does), Enter follows the row link or fires mm-row-activate, and on a
cell with buttons or links Enter (or F2) moves into it, Tab cycles its controls and Escape
returns to the cell. Ctrl+A (⌘A) selects every row.
Cells render inside the table's shadow root, so page stylesheets do not reach templates from a
column's render. Prefer elements (mm-link, mm-text, mm-tag, mm-status-pill), which
carry their own styles. The table also styles this set of classes inside cells, matching
@marmotdata/css: mm-link (ink in cells, underlined on hover and focus), mm-code-inline and
code, mm-kbd, mm-dense, mm-label, mm-caption, plus two cell helpers: mm-mono (mono
dense, for IDs and keys only) and mm-muted (mm-text-3, which steps up to mm-text-2 in a selected
row). Number columns align their header with their values.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
columns | DataTableColumn<any>[] | [] | Column definitions, in their initial order. | |
rows | readonly object[] | [] | Row objects. | |
rowKey | row-key | string | 'id' | Field that uniquely identifies a row, used for selection. |
label | label | string | '' | Accessible name for the grid, e.g. "Assets". |
selectable | selectable | boolean | false | Adds a checkbox column and row selection. |
selected | unknown[] | [] | Keys (row-key values) of the selected rows. | |
sort | DataTableSort | null | null | Current sort. Set it to sort initially; it updates as headers are activated. | |
manualSort | manual-sort | boolean | false | Do not sort rows locally: only update sort and fire mm-sort-change (server-side sorting). |
reorderable | reorderable | boolean | false | Lets people drag headers (or press Alt+Shift+Left/Right) to reorder columns. |
stickyFirstColumn | sticky-first-column | boolean | false | Keeps the first data column (and the checkbox column) in view while scrolling sideways. |
density | density | 'default' | 'compact' | undefined | compact (36px rows) or default (40px). Unset follows data-density on an ancestor. | |
loading | loading | boolean | false | Shows skeleton rows (after 200ms, for at least 400ms) and marks the grid busy. |
error | error | string | '' | An error message. Shows the error state with a retry button instead of rows. |
emptyHeading | empty-heading | string | '' | Empty state title. |
emptyText | empty-text | string | '' | Empty state sentence: what is missing and what to do about it. |
rowDisabled | ((row: any) => boolean) | undefined | Rows for which this returns true are shown disabled: not selectable or activatable. | ||
rowLabel | ((row: any) => string) | undefined | Accessible name of a row's checkbox. Defaults to "Select row". | ||
viewRows | readonly object[] | Rows in display order (after local sorting). |
Methods
| Method | Description |
|---|---|
focus() | Moves focus to the active cell (the grid's tab stop). |
scrollToRow() | Scrolls a row (by display index) into view. |
Events
| Event | Detail | Description |
|---|---|---|
mm-sort-change | CustomEvent<DataTableSortChangeDetail> | A sortable header was activated. direction is null when sorting was cleared. |
mm-selection-change | CustomEvent<DataTableSelectionChangeDetail> | Rows were selected or deselected. |
mm-row-activate | CustomEvent<DataTableRowActivateDetail> | A row was clicked or Enter was pressed on it. |
mm-column-resize | CustomEvent<DataTableColumnResizeDetail> | A column was resized by pointer or keyboard. |
mm-column-reorder | CustomEvent<DataTableColumnReorderDetail> | A column was moved. |
mm-retry | CustomEvent<Record<string, never>> | The retry button in the error state was activated. |
Slots
| Slot | Description |
|---|---|
empty | Shown when there are no rows. Defaults to an mm-empty-state built from empty-heading and empty-text. |
error-actions | Replaces the default retry button in the error state. |
CSS parts
| Part | Description |
|---|---|
base | The bordered surface. |
scroller | The scroll container. |
grid | The element with role="grid". |
header | The header row. |
row | Each body row. |
cell | Each body cell. |
CSS custom properties
| Property | Description |
|---|---|
--mm-data-table-max-height | Height at which the table scrolls. Defaults to 560px. To fill a fixed-height container, give the element a height and set this to none. |
Examples
Every state of mm-data-table in Storybook, with keyboard and screen reader tests.