Foundation component · Data display

Data Table

A compact, read-only table for comparing records and scanning summaries.

Live playground

When to use it

  • Use Data Table for records with stable column headings and several comparable rows.
  • Keep row selection optional. When enabled, a quiet full-row fill and the shared Selection Toolbar make the selected set clear without turning each cell into a separate tile.
  • Data rows have no inter-row gap. Group surfaces have 2px above and below; adjacent selected rows form one surface with rounded corners only at the outer boundary.
  • Render each data group in its own DataTable.Body with a DataTable.GroupRow. The Consumer decides group membership and counts.
  • Keep row data, optional selection, sorting, filtering, and summaries in the Consumer. The shared component owns table semantics, compact cell geometry, pinned columns, and column resizing.

  • Use Description List for facts about a single object. Use Data Grid when cells need editing or two-dimensional keyboard navigation.

Interaction and layout

  • Pin only a leading set of columns. At narrow widths, the remaining columns scroll horizontally.

  • Resizable column separators support pointer drag and Left/Right arrow keys. Give each separator a meaningful label.

  • A sortable DataTable.Head shows a compact hover/focus surface. Pass onSort, sortDirection, and sortLabel; the Consumer owns the ordering.
  • For a row-actions column, keep the header cell and give it an accessible name with aria-label. Leave its visible content empty by default; render a label when the product needs one.
  • Reveal row selection and row actions on hover or keyboard focus, and keep them visible for selected rows. On touch, keep these controls visible.
  • Header and footer remain visible within a height-constrained table. Put calculated values or actions in footer cells only when they help compare the collection.

Implementation

The Consumer supplies data and controls selection. Column widths and pinned offsets stay aligned as a user resizes.

import { DataTable } from "@lenso/ui/data-table";

const columns = [
  { id: "name", width: 240, minWidth: 160, pinned: true },
  { id: "status", width: 160 },
];

<DataTable.Root columns={columns} label="Projects" maxHeight={480}>
  <DataTable.Header>
    <DataTable.Row>
      <DataTable.Head columnId="name" resizable resizeLabel="Resize Name column">
        Name
      </DataTable.Head>
      <DataTable.Head columnId="status">Status</DataTable.Head>
    </DataTable.Row>
  </DataTable.Header>
  <DataTable.Body>
    <DataTable.GroupRow label="Active" count={1} />
    <DataTable.Row>
      <DataTable.Cell columnId="name">Atlas</DataTable.Cell>
      <DataTable.Cell columnId="status">Active</DataTable.Cell>
    </DataTable.Row>
  </DataTable.Body>
</DataTable.Root>;
Lenso UI