StartFoundationsComponentsContent

Data table

Data table renders a table from your rows and column definitions, with sorting, column resizing, row selection, expandable rows, and pagination.

Stable Figma
OverviewUsageAPIAccessibility
<tk-data-table label="Quarterly revenue by region" hover></tk-data-table>
<tk-data-table id="revenue" label="Quarterly revenue by region" hover></tk-data-table>
<script>
  const table = document.getElementById('revenue');
  table.columns = [
    { key: 'region', header: 'Region', sortable: true },
    { key: 'lead', header: 'Lead', resizable: true, width: 220 },
    { key: 'revenue', header: 'Revenue', numeric: true, sortable: true },
  ];
  table.data = [
    { id: 'emea', region: 'EMEA', lead: 'Alice Chen', revenue: 1204 },
    { id: 'apac', region: 'APAC', lead: 'Bo Nguyen', revenue: 987 },
    { id: 'amer', region: 'Americas', lead: 'Carla Diaz', revenue: 2130 },
  ];
</script>

Anatomy

Part What it is
tk-data-table the element; takes data and columns as JS properties
caption hidden; it holds label and names the table
sort button a sortable column's header
resize handle the edge of a resizable column; drag it or use the arrow keys
selection column a "select all" checkbox in the header and one checkbox per row
expand button a chevron in the first cell of a row with children
scroll part the scroll container

Columns

Each column is an object:

{
  key: string;              // the field on each row, and the column id
  header: string;           // the header text
  align?: 'start' | 'center' | 'end';
  numeric?: boolean;        // end-aligned, tabular figures
  sortable?: boolean;
  resizable?: boolean;
  width?: number;           // initial width in px; unset shares the free space
  minWidth?: number;        // resize limit, default 48
  maxWidth?: number;        // resize limit, default 800
}

Leave at least one column without width so the table fills its container. If every column has a width, the table is exactly as wide as their sum.

Sorting

Set sortable: true on a column. Clicking its header cycles ascending, descending, and unsorted. Read the current sort from sort.

Each click fires tk-sort first. Call preventDefault() on it to stop the table from sorting. The header then keeps its previous sort indicator.

table.addEventListener('tk-sort', (e) => {
  console.log(e.detail); // { key: 'revenue', direction: 'asc' }
});

Column resizing

Set resizable: true on a column. Drag the handle at its edge, or focus the handle and use the arrow keys: ← and → change the width by 8px, Home and End jump to minWidth and maxWidth. While you hover, focus, or drag a handle, a line runs down the whole table at the column edge.

Each finished change fires tk-column-resize. Read the widths from columnSizing.

Row selection

selectable adds a checkbox to every row and a "select all" checkbox to the header. "Select all" selects every row, on every page. Selecting a parent row selects its children.

Set row-key to a field that is unique and stable on every row; selection and expanded rows are stored by it. Read the selection from selectedKeys, and listen for tk-selection-change.

Expandable rows

Set expandable and put child rows under children, or the field you name in children-key. Rows with children get an expand button, and their children are indented. Children sort inside their parent.

table.expandable = true;
table.data = [
  { id: 'emea', name: 'EMEA', headcount: 120, children: [
    { id: 'uk', name: 'United Kingdom', headcount: 45 },
  ]},
  { id: 'amer', name: 'Americas', headcount: 200 },
];

Pagination

Set page-size and only the current page renders. The page controls are a Pagination you place and connect: render one item per page from pageCount, and set page (counted from 0) when one is selected.

<tk-data-table id="records" label="Records" page-size="10"></tk-data-table>
<tk-pagination id="pages" label="Records pages"></tk-pagination>
<script>
  const table = document.getElementById('records');
  const pages = document.getElementById('pages');
  const renderPages = () => {
    pages.innerHTML = Array.from({ length: table.pageCount }, (_, i) =>
      `<tk-pagination-item value="${i + 1}" ${i === table.page ? 'current' : ''}>${i + 1}</tk-pagination-item>`,
    ).join('');
  };
  pages.addEventListener('tk-page-select', (e) => {
    table.page = Number(e.detail.value) - 1;
    renderPages();
  });
  renderPages();
</script>

Column order

Set columnOrder to an array of column keys to reorder the columns. People can't drag columns to reorder them.