Table Data

<sl-table-data> | SlTableData
Since 2.1 stable

Data table component to display data in a tabular format.

Examples

Data table

The data table displays tabular data using a column configuration array. Each column is defined as an object that controls its label, cell type, alignment, width, sorting, and formatting.

By default, the table includes:

  • Row selection with checkboxes
  • Column sorting (when enabled per column)
  • Inline editable, button, and progress bar cells via the column type
  • Expandable rows for nested data
<sl-table-data id="data-table-default"></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-default');

  dataTable.chartTitle = 'Special data table';
  dataTable.rowHover = true;
  dataTable.striped = true;

  dataTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Age', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'City', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'input', label: 'Editable input', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false },
    { type: 'button', label: 'Action', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false }
  ];

  dataTable.data = [
    { id: 1, rows: ['Alice', 12, 'New York', 'A value that needs to be edited', 'View'] },
    { id: 2, rows: ['Jim', 38, 'Victoria', 'A value that needs to be edited', 'View'] },
    { id: 3, rows: ['Bob', 50, 'Western AUS', 'A value that needs to be edited', 'View'] },
    { id: 4, rows: ['Peter', 29, 'TN', 'A value that needs to be edited', 'View'] },
    { id: 5, rows: ['Joe', 30, 'NSW', 'A value that needs to be edited', 'View'] }
  ];

  dataTable.addEventListener('cell-edit', (event) => {
    const { id, rowId, colIndex, updatedValue } = event.detail;
    console.log(`Cell updated record id: ${id} at row ${rowId}, column ${colIndex}: ${updatedValue}`);
  });

  dataTable.addEventListener('selection-change', (event) => {
    const { selectedRowIds } = event.detail;
    console.log(`Ids of rows selected: ${selectedRowIds}`);
  });
</script>

Column configuration

Each item in the columns array supports the following properties:

Property Type Description
type 'text' | 'button' | 'input' | 'progress-bar' Determines how cell values are rendered
label string Header label displayed in the table
alignment 'left' | 'center' | 'right' Horizontal alignment of header and cell content
buttonVariant Button variants Determine button variants
width string Fixed column width (e.g. '200px')
minWidth string Minimum column width
maxWidth string Maximum column width
formatOptions object Intl.NumberFormat options passed to toLocaleString for text columns
sortable boolean When true, clicking the header sorts the column

Hiding the checkbox column

Use hide-checkbox-column to hide the row selection checkboxes.

<sl-table-data id="data-table-hiding" hide-checkbox-column></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-hiding');

  dataTable.chartTitle = 'Data table with hidden columns';
  dataTable.rowHover = true;
  dataTable.striped = true;

  dataTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Age', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'City', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true }
  ];

  dataTable.data = [
    { id: 1, rows: ['Alice', 12, 'New York'] },
    { id: 2, rows: ['Jim', 38, 'Chicago'] },
    { id: 3, rows: ['Bob', 50, 'Los Vegas'] }
  ];
</script>

Text columns with formatting

text columns render cell values using toLocaleString('en-AU', formatOptions). Use formatOptions to control number, currency, or date formatting.

<sl-table-data id="data-table-format" hide-checkbox-column></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-format');

  dataTable.chartTitle = 'Data table with formatted values';
  dataTable.rowHover = true;

  dataTable.columns = [
    { type: 'text', label: 'Asset', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    {
      type: 'text',
      label: 'Value',
      alignment: 'right',
      width: '',
      minWidth: '',
      maxWidth: '',
      formatOptions: { style: 'currency', currency: 'AUD' },
      sortable: true
    }
  ];

  dataTable.data = [
    { id: 1, rows: ['Australian equities', 860611.01] },
    { id: 2, rows: ['Global equities', 1458989] },
    { id: 3, rows: ['Cash', 123130] }
  ];
</script>

Button columns

Set a column’s type to 'button' to render a tertiary button by default in each cell, or specify the variant with buttonVariant. The cell value is used as the button label; Button will not be render when empty, otherwise, the button-label attribute value is used instead.

<sl-table-data id="data-table-button" button-label="View"></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-button');

  dataTable.chartTitle = 'Data table with button column';
  dataTable.rowHover = true;
  dataTable.striped = true;

  dataTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Age', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'button', label: 'Action', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false, buttonVariant: 'primary' }
  ];

  dataTable.data = [
    { id: 1, rows: ['Alice', 12, 'Details'] },
    { id: 2, rows: ['Jim', 38, 'Edit'] },
    { id: 3, rows: ['Bob', 50, ''] },
    { id: 4, rows: ['Peter', 58, '     '] },
    { id: 5, rows: ['Jenny', 42] }
  ];

  dataTable.addEventListener('select', (event) => {
    const { rowId, colIndex } = event.detail;
    console.log(`Button clicked at row ${rowId}, column ${colIndex}`);
  });
</script>

Input columns

Set a column’s type to 'input' to allow inline editing. The component emits a cell-edit event when the user finishes editing a cell (on blur).

<sl-table-data id="data-table-input"></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-input');

  dataTable.chartTitle = 'Data table with editable column';
  dataTable.rowHover = true;

  dataTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'input', label: 'Notes', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false }
  ];

  dataTable.data = [
    { id: 1, rows: ['Alice', 'A value that needs to be edited'] },
    { id: 2, rows: ['Jim', 'Another editable value'] },
    { id: 3, rows: ['Bob', 'Update me on blur'] }
  ];

  dataTable.addEventListener('cell-edit', (event) => {
    const { id, rowId, colIndex, updatedValue } = event.detail;
    console.log(`Cell updated for id ${id}: ${updatedValue}`);
  });
</script>

Progress bar columns

Set a column’s type to 'progress-bar' to render an sl-progress-bar in each cell. Cell values must be numbers from 0–100; non-numeric values render an empty cell. The indicator color is chosen automatically based on the value: danger (≤ 70), warning (> 70), or success (> 90). The current value is shown on the bar.

<sl-table-data id="data-table-progress-bar" hide-checkbox-column></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-progress-bar');

  dataTable.chartTitle = 'Data table with progress bar column';
  dataTable.rowHover = true;

  dataTable.columns = [
    { type: 'text', label: 'Goal', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'progress-bar', label: 'Progress', alignment: 'left', width: '200px', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true }
  ];

  dataTable.data = [
    { id: 1, rows: ['Revenue target', 95] },
    { id: 2, rows: ['Client onboarding', 78] },
    { id: 3, rows: ['Training completion', 45] }
  ];
</script>

Sortable columns

Sorting is controlled per column via the sortable property. Click a sortable header to toggle between ascending and descending order. Columns without sortable: true are not sortable.

<sl-table-data id="data-table-sortable"></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-sortable');

  dataTable.chartTitle = 'Data table with sortable columns';
  dataTable.rowHover = true;
  dataTable.striped = true;

  dataTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Age', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'input', label: 'Editable input', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false }
  ];

  dataTable.data = [
    { id: 1, rows: ['Alice', 12, 'A value that needs to be edited'] },
    { id: 2, rows: ['Jim', 38, 'A value that needs to be edited'] },
    { id: 3, rows: ['Bob', 50, 'A value that needs to be edited'] }
  ];
</script>

Nested table

Table rows can be expanded when nestedData and nestedColumns are provided on a row. The nested table is rendered inside the expanded row with the checkbox column hidden.

A maximum height is applied to the nested table body; content that exceeds this height scrolls vertically.

<sl-table-data id="nested-table-example" hide-checkbox-column></sl-table-data>
<script>
  const dataTable = document.getElementById('nested-table-example');

  dataTable.chartTitle = 'Nested data table';

  dataTable.columns = [
    { type: 'text', label: 'Code', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Quantity', alignment: 'right', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Price valuation date', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false },
    { type: 'text', label: 'Gross Capital Gain/(Loss)', alignment: 'right', width: '', minWidth: '', maxWidth: '', formatOptions: { style: 'currency', currency: 'AUD' }, sortable: false }
  ];

  const nestedColumns = [
    { type: 'text', label: 'Parcel date', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false },
    { type: 'text', label: 'Accounting cost base', alignment: 'right', width: '', minWidth: '', maxWidth: '', formatOptions: { style: 'currency', currency: 'AUD' }, sortable: false },
    { type: 'text', label: 'Cost base', alignment: 'right', width: '', minWidth: '', maxWidth: '', formatOptions: { style: 'currency', currency: 'AUD' }, sortable: false },
    { type: 'text', label: 'Market Value', alignment: 'right', width: '', minWidth: '', maxWidth: '', formatOptions: { style: 'currency', currency: 'AUD' }, sortable: false },
    { type: 'text', label: 'Gross Capital Gain/(Loss)', alignment: 'right', width: '', minWidth: '', maxWidth: '', formatOptions: { style: 'currency', currency: 'AUD' }, sortable: false }
  ];

  dataTable.data = [
    {
      id: 1,
      rows: ['ANZ', 'Australia and New Zealand Banking Group', 100, '12/04/2026', 1000],
      nestedColumns,
      nestedData: [
        { id: 1, rows: ['22/01/2026', 1000, 1000, 1000, 1000] },
        { id: 2, rows: ['22/01/2026', 1000, 1000, 1000, 1000] },
        { id: 3, rows: ['22/01/2026', 1000, 1000, 1000, 1000] }
      ]
    },
    {
      id: 2,
      rows: ['APA', 'APA Group', 50, '12/04/2026', 500],
      nestedColumns,
      nestedData: [
        { id: 1, rows: ['22/01/2026', 500, 500, 500, 500] },
        { id: 2, rows: ['22/01/2026', 500, 500, 500, 500] }
      ]
    },
    { id: 3, rows: ['CBA', 'Commonwealth Bank', 75, '12/04/2026', 750] },
    { id: 4, rows: ['CH01', 'Charter Hall', 25, '12/04/2026', 250] }
  ];
</script>

Column widths

Set width, minWidth, and/or maxWidth on individual column definitions to control column sizing. When any column has a width set, the table uses fixed layout.

<sl-table-data id="parent-table" hide-checkbox-column></sl-table-data>
<script>
  const parentTable = document.getElementById('parent-table');

  parentTable.chartTitle = 'Table with custom column widths';
  parentTable.rowHover = true;
  parentTable.striped = true;

  parentTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '300px', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Age', alignment: 'left', width: '100px', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'City', alignment: 'left', width: '200px', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false }
  ];

  parentTable.data = [
    { id: 1, rows: ['Alice', 25, 'New York'] },
    { id: 2, rows: ['Bob', 30, 'Los Angeles'] },
    { id: 3, rows: ['Charlie', 35, 'Chicago'] }
  ];
</script>

Nested table column widths

Apply widths directly on each column in the nestedColumns array passed to expandable rows.

<sl-table-data id="nested-table-custom-width" hide-checkbox-column></sl-table-data>
<script>
  const dataTable = document.getElementById('nested-table-custom-width');

  dataTable.chartTitle = 'Nested table with custom column widths';

  dataTable.columns = [
    { type: 'text', label: 'Code', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true }
  ];

  const nestedColumns = [
    { type: 'text', label: 'Parcel date', alignment: 'left', width: '300px', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false },
    { type: 'text', label: 'Cost base', alignment: 'right', width: '300px', minWidth: '', maxWidth: '', formatOptions: { style: 'currency', currency: 'AUD' }, sortable: false },
    { type: 'text', label: 'Market Value', alignment: 'right', width: '300px', minWidth: '', maxWidth: '', formatOptions: { style: 'currency', currency: 'AUD' }, sortable: false }
  ];

  dataTable.data = [
    {
      id: 1,
      rows: ['ANZ', 'Australia and New Zealand Banking Group'],
      nestedColumns,
      nestedData: [
        { id: 11, rows: ['22/01/2026', 1000, 1050] },
        { id: 12, rows: ['23/01/2026', 1200, 1180] }
      ]
    }
  ];
</script>

Column alignment

Set alignment to 'left', 'center', or 'right' on each column definition. Alignment applies to both the header and body cells.

<sl-table-data id="data-table-alignment" hide-checkbox-column></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-alignment');

  dataTable.chartTitle = 'Data table with custom column alignment';
  dataTable.rowHover = true;
  dataTable.striped = true;

  dataTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Age', alignment: 'center', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'City', alignment: 'right', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false }
  ];

  dataTable.data = [
    { id: 1, rows: ['Alice', 25, 'Sydney'] },
    { id: 2, rows: ['Bob', 38, 'Melbourne'] },
    { id: 3, rows: ['Charlie', 29, 'Brisbane'] }
  ];
</script>

Customising built-in column widths

The checkbox column can be resized using a CSS custom property on the component.

<sl-table-data
  id="data-table-css-widths"
  style="--table-data-checkbox-column-width: 56px;"
></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-css-widths');

  dataTable.chartTitle = 'Table with custom checkbox column width';
  dataTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false }
  ];
  dataTable.data = [
    { id: 1, rows: ['Alice'] },
    { id: 2, rows: ['Bob'] }
  ];
</script>

Row hover and striped rows

Use row-hover to highlight rows on hover and striped to apply alternating row backgrounds.

<sl-table-data id="data-table-styles" row-hover striped></sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-styles');

  dataTable.columns = [
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false },
    { type: 'text', label: 'Age', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: false }
  ];
  dataTable.data = [
    { id: 1, rows: ['Alice', 12] },
    { id: 2, rows: ['Jim', 38] },
    { id: 3, rows: ['Bob', 50] }
  ];
</script>

Table title and action slot

Use the table-title attribute (or chartTitle property) to display a heading above the table. Place action buttons in the default slot to render them beside the title.

Export
<sl-table-data id="data-table-slot" table-title="Portfolio holdings">
  <sl-button size="small" variant="secondary">Export</sl-button>
</sl-table-data>
<script>
  const dataTable = document.getElementById('data-table-slot');

  dataTable.columns = [
    { type: 'text', label: 'Code', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true },
    { type: 'text', label: 'Name', alignment: 'left', width: '', minWidth: '', maxWidth: '', formatOptions: {}, sortable: true }
  ];
  dataTable.data = [
    { id: 1, rows: ['ANZ', 'Australia and New Zealand Banking Group'] },
    { id: 2, rows: ['CBA', 'Commonwealth Bank'] }
  ];
</script>

[component-metadata:sl-table-data]

Slots

Name Description
(default) The default slot.

Learn more about using slots.

Properties

Name Description Reflects Type Default
data The data to be displayed in the table. TableData[] []
columns The titles to be displayed in the table header columns. Column[] []
chartTitle
table-title
The title of the data table. string ''
rowHover
row-hover
Adds hover effect to the table rows. boolean false
striped Adds alternating stripes to the table rows. boolean false
hideCheckboxColumn
hide-checkbox-column
Hides the checkbox selection column. boolean false
buttonLabel
button-label
Default label used when button cell doesn’t override it. string 'Label'
buttonVariant
button-variant
Default button variant used when button cell doesn’t override it. SlButton['variant'] 'tertiary'
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about attributes and properties.

Events

Name React Event Description Event Detail
cell-edit event emitted when a cell is edited. -
select event emitted when the selection changes. -

Learn more about events.

Custom Properties

Name Description Default
--table-data-checkbox-column-width The customisable width of the checkbox column.

Learn more about customizing CSS custom properties.

Parts

Name Description
base The component’s base wrapper.
table The table element.

Learn more about customizing CSS parts.

Dependencies

This component automatically imports the following dependencies.

  • <sl-checkbox>
  • <sl-icon>
  • <sl-icon-button>
  • <sl-input>