Skip to main content

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​

PropertyAttributeTypeDefaultDescription
columnsDataTableColumn<any>[][]Column definitions, in their initial order.
rowsreadonly object[][]Row objects.
rowKeyrow-keystring'id'Field that uniquely identifies a row, used for selection.
labellabelstring''Accessible name for the grid, e.g. "Assets".
selectableselectablebooleanfalseAdds a checkbox column and row selection.
selectedunknown[][]Keys (row-key values) of the selected rows.
sortDataTableSort | nullnullCurrent sort. Set it to sort initially; it updates as headers are activated.
manualSortmanual-sortbooleanfalseDo not sort rows locally: only update sort and fire mm-sort-change (server-side sorting).
reorderablereorderablebooleanfalseLets people drag headers (or press Alt+Shift+Left/Right) to reorder columns.
stickyFirstColumnsticky-first-columnbooleanfalseKeeps the first data column (and the checkbox column) in view while scrolling sideways.
densitydensity'default' | 'compact' | undefinedcompact (36px rows) or default (40px). Unset follows data-density on an ancestor.
loadingloadingbooleanfalseShows skeleton rows (after 200ms, for at least 400ms) and marks the grid busy.
errorerrorstring''An error message. Shows the error state with a retry button instead of rows.
emptyHeadingempty-headingstring''Empty state title.
emptyTextempty-textstring''Empty state sentence: what is missing and what to do about it.
rowDisabled((row: any) => boolean) | undefinedRows for which this returns true are shown disabled: not selectable or activatable.
rowLabel((row: any) => string) | undefinedAccessible name of a row's checkbox. Defaults to "Select row".
viewRowsreadonly object[]Rows in display order (after local sorting).

Methods​

MethodDescription
focus()Moves focus to the active cell (the grid's tab stop).
scrollToRow()Scrolls a row (by display index) into view.

Events​

EventDetailDescription
mm-sort-changeCustomEvent<DataTableSortChangeDetail>A sortable header was activated. direction is null when sorting was cleared.
mm-selection-changeCustomEvent<DataTableSelectionChangeDetail>Rows were selected or deselected.
mm-row-activateCustomEvent<DataTableRowActivateDetail>A row was clicked or Enter was pressed on it.
mm-column-resizeCustomEvent<DataTableColumnResizeDetail>A column was resized by pointer or keyboard.
mm-column-reorderCustomEvent<DataTableColumnReorderDetail>A column was moved.
mm-retryCustomEvent<Record<string, never>>The retry button in the error state was activated.

Slots​

SlotDescription
emptyShown when there are no rows. Defaults to an mm-empty-state built from empty-heading and empty-text.
error-actionsReplaces the default retry button in the error state.

CSS parts​

PartDescription
baseThe bordered surface.
scrollerThe scroll container.
gridThe element with role="grid".
headerThe header row.
rowEach body row.
cellEach body cell.

CSS custom properties​

PropertyDescription
--mm-data-table-max-heightHeight 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.