Data Display

DataTable

Data-driven convenience wrapper over the compositional Table family that renders a full semantic table from a columns array and a rows array.

import { DataTable } from '@lostgradient/cinder/data-table';
tabledata
01

Overview

Data-driven accessible table that renders rows and columns into a real <table> with <caption>, scoped column and row headers, optional sortable columns, and a horizontal-scroll responsive container.

Usage

svelte
<script lang="ts">
  import { DataTable } from '@lostgradient/cinder/data-table';

  const columns = [
    { key: 'name', label: 'Name', rowHeader: true },
    { key: 'score', label: 'Score', sortable: true, align: 'end' },
  ];
  const rows = [
    { name: 'Ada', score: 98 },
    { name: 'Grace', score: 95 },
  ];
</script>

<DataTable {columns} {rows} caption="Class roster" />

Guidance

Set virtualized with a fixed rowHeight when the table is rendering thousands of same-height append-only rows. The virtualized path keeps the <table>, <thead>, and <tbody> semantics intact, sets aria-rowcount to the full logical row count, and annotates mounted body rows with full-table aria-rowindex values. Variable or measured body row heights are out of scope for this mode.

Use When

  • Rendering a roster, cohort, or assignment view from row/column data with real table semantics.
  • Needing sortable columns with screen-reader-announced sort state out of the box.

Avoid When

  • Composing a bespoke table layout with custom cell markup — use the compositional Table family directly.
  • Visualizing dense numeric magnitude across two dimensions — use matrix-chart instead.
Live preview
02

When to use

Use when
  • Rendering a structured dataset where columns and rows are known at runtime (e.g. API responses, config-driven dashboards).
  • You want correct scope=col / scope=row semantics and aria-sort wiring without writing Table.Header / Table.Body manually.
Avoid when
  • You need custom cell rendering, interactive cells, nested components, or column spanning — use the compositional Table family directly.
  • You need fully custom cell or row composition — use Table directly.
03

Examples

Class roster

A class roster with a sortable grade column. The Name column is the row header (scope="row"). Bind sort state and reorder rows in a $derived expression.

Sortable data table

Multiple sortable columns with a scrollable wrapper. Sort state is bound and rows are reordered via $derived.

Virtualized data table

A semantic table that windows 10,000 fixed-height body rows while preserving full row counts and row indexes.

04

Props

Props for data-table
Name Type Default Required Bindable Description
columns DataTableColumn<Row>[] req Column descriptors defining the headers and cell rendering for each column.
rows Row[] req Row data. Each entry is read via column.key for each column.
caption text Visual caption rendered as a <caption> element above the table.
sort TableSort | undefined bind Bound sort state. When the user activates a sortable header cell, this prop is updated with the new { column, direction }. The consumer is responsible for reordering rows in response — DataTable does not sort internally. Pass undefined initially when no column is sorted; the component will never write back undefined itself (sort always toggles to a column).
selectable 'none' | 'single' | 'multiple' 'none' Enables checkbox-based row selection. "none" renders no selection controls, "single" allows one selected row id, and "multiple" allows any number. Selection state is exposed through row checkbox controls; native table rows do not emit aria-selected. Defaults to "none".
selectedRowIds string[] | Set<string> [] bind Bound selected row ids. Arrays stay arrays on update; Sets stay Sets. When omitted, DataTable starts with an empty array.
getRowId (row: Row, index: number) => string Resolves the stable row id used for selection. Defaults to row.id when it is a string or number, otherwise the row's current positional index.
isRowSelectionDisabled (row: Row, index: number) => boolean Returns true when a row should render a disabled selection checkbox.
selectAllLabel text 'Select all rows' Accessible label for the multiple-selection header checkbox.
rowSelectionLabel (row: Row, index: number) => string Accessible label for an individual row selection checkbox.
stickyHeader boolean false When true, the header sticks to the top of the scrolling container.
density 'comfortable' | 'condensed' | 'spacious' 'comfortable' Vertical padding density for header and body cells. Defaults to 'comfortable'.
scrollable boolean false When true, wraps the table in a .cinder-table-scroll container that enables horizontal overflow scrolling on small viewports.
virtualized boolean false When true, renders only the visible <tbody> row window plus spacer rows. Requires a fixed row height. This is intended for large, append-only tables such as live logs or event streams.
rowHeight number 44 Fixed body row height in pixels for virtualized mode. This must match the actual rendered body row height, including density padding and any wrapping introduced by the table content. Defaults to 44.
overscan number 5 Extra body rows rendered before and after the visible virtualized window. Defaults to 5.
height text '24rem' CSS block-size for the virtualized native scroll container. Defaults to "24rem" when virtualized is true.
stickToBottom boolean false When true in virtualized mode, appending rows while scrolled to the bottom keeps the newest row pinned in view. Appending while scrolled up does not change the viewport.
onscroll unknown req