Table
Displays structured data in a row-and-column grid with sorting, row selection, multi-select, and a loading state. It can be wired together with a pagination component.
columns / data support a declarative attribute channel: pass a JSON string directly to render the header and data rows (invalid JSON falls back to the empty state), while the property channel (assigning arrays/objects, property takes precedence) remains available and can be serialized into an SSR snapshot.
Basic Usage (with Sorting)
Sortable columns
Click a sortable column header to cycle through ascending / descending / no sort.
Density Sizes
size: small / medium (default) / large
Each size only changes the default cell padding and font size, all via CSS variables: override with --oas-table-cell-padding-block / --oas-table-cell-padding-inline / --oas-table-font-size (takes precedence over the size preset). Invalid values fall back to medium with a warning. row-height is orthogonal to size: in fixed-row-height scenarios such as virtual scrolling, the row height is controlled by row-height and is not affected by the size preset.
Column Alignment and Width
Alignment and width
Controlled Sorting and Row Selection
Initial sort and selection
sort-key / sort-order control the sort; selected highlights the selected row (clicking a row toggles the selection).
Multi-Select
Row multi-select (checkable)
The header checkbox selects / clears all rows at once; row checkboxes toggle individually. Selection changes emit oas-check.
Integration with Pagination
Table + pagination
Table data is sliced into 5 rows per page; on page change the data attribute is updated via the oas-change event and the table re-renders.
Fixed Columns
Left fixed column
Left/right fixed columns + sticky header
In the column config, fixed: 'left' | 'right' makes that column's header and cells position: sticky (the left / right offset is accumulated automatically from column widths); the remaining columns scroll horizontally, and the header always stays sticky.
Inline Editing
Double-click cells to edit (with operation column)
Last edit: —
Set editable to enable inline editing; mark editable columns with editable: true on the column config (editor: 'input' for text / editor: 'select' for a dropdown with editOptions), and actions: true renders an operation column (Edit / Save / Cancel buttons). Double-click a cell, or focus it and press Enter / F2, to start editing: Enter or blur submits, Esc cancels; submit emits oas-edit (detail: { rowIndex, key, column, value }), cancel emits oas-edit-cancel. Submitting an empty value restores the original (non-destructive by default).
Controlled Editing
Controlled editing (edit-controlled)
This table is in controlled mode: the component does not write back data on submit; the host listens to oas-edit and updates it.
edit-controlled is the controlled editing mode: on submit the component does not write back data, it only emits oas-edit; the host listens and updates data itself (see the script below).
Sticky Rows
Sticky header + first N rows
sticky-rows="N" sticks the first N rows below the header (works with the scroll container: set height so the table body scrolls); it coexists with fixed columns (fixed: 'left' | 'right').
Large Data Sets (Virtual Scroll)
Virtual scroll with 10k rows
Setting height enables virtual scrolling (with a fixed row-height): only rows within the visible window are rendered. It works together with fixed columns, sorting, and multi-select; scrolling emits oas-scroll.
Stripes and Borders
Striped rows (stripe)
Full border (bordered)
Set stripe to alternate the background of odd/even rows, or bordered to draw a full grid border around the cells.
Summary Row
Summary row (summary)
The summary attribute is a JSON array [{ key, type: 'sum' | 'avg' | 'count', label? }] rendered as a summary row at the table footer: label is shown in the first non-aggregated column, and each aggregated value in its corresponding column. You can also write summary: 'sum' | 'avg' | 'count' directly on a column config.
Expandable Rows
Expandable rows (expand field)
When a row's data has a non-empty expand field, an expand column appears at the end of the table; clicking the button expands the whole row to show custom content. The expanded state is stored in the expanded attribute (a comma-separated set of keys), and toggling emits oas-expand.
Controlled expansion (expanded attribute)
expanded is a controlled attribute (a comma-separated set of keys): pre-expanded rows open on the first render, and the host can add or remove keys at any time to drive the expansion state (shared by tree parent rows and expandable rows).
Tree Data
Tree data (children)
Rows with a children array are rendered as a tree: parent rows show an expand button in the first column, and child rows are indented by depth. The expanded state is stored in the same expanded attribute, and toggling emits oas-expand.
Loading State
Loading state
With the loading attribute, the header stays visible and the data area shows placeholder rows; removing the attribute restores the data.
Empty State
Empty data
Custom empty text
Events
Sort and click events
Sort: none · Row: —
API
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
bordered | Full border: draws a grid outline around cells (the outer frame is built in) | — | — |
checkable | Enables checkbox multi-select | boolean | — |
columns | Column config [{ key, title, sortable?, width?, align?, fixed?, render?, summary?, editable?, editor?, editOptions?, actions? }], JSON string (declarative attribute channel; property assignment takes precedence) | TableColumn[] | string | [] |
data | Row data [{ [key]: value, children?, expand? }], JSON string (declarative attribute channel; property assignment takes precedence) | Array<Record<string, unknown>> | string | [] |
edit-controlled | Controlled editing: does not write back data on submit, only fires oas-edit; the host listens and updates data itself | boolean | — |
editable | Inline editing switch (requires editable: true on columns; same for the actions: true operation column) | boolean | — |
empty-text | Empty state text | — | — |
expanded | Set of expanded row keys (comma-separated; shared by tree parent rows and expandable rows) | string | — |
height | Virtual scroll viewport height (px); when set, only visible-window rows plus head/tail placeholders are rendered | string | 320 |
loading | Loading state: shows placeholder rows in the data area (header retained) | boolean | — |
row-height | Fixed row height for virtual scrolling (px) | string | 40 |
row-key | Unique key field of a row | string | key |
selected | Set of selected row keys (comma-separated) | string | — |
size | Density preset: small / medium (default) / large — only changes default cell padding and font size (all via CSS variables; override with --oas-table-cell-padding-block / --oas-table-cell-padding-inline / --oas-table-font-size, which take precedence); invalid values fall back to medium with a warning; orthogonal to row-height | string | medium |
sort-key | Controlled sort; sort-order is asc / desc / empty | string | — |
sort-order | Controlled sort; sort-order is asc / desc / empty | SortOrder | — |
sticky-rows | Number of sticky rows (N): the first N rows stick below the header (coexists with the scroll container and fixed columns) | string | — |
stripe | Zebra striping: alternating light background for odd/even rows | boolean | — |
summary | Summary config [{ key, type: 'sum'|'avg'|'count', label? }], JSON string | string | — |
Events
| Event | Description |
|---|---|
oas-check | Checkbox selection change, detail: { keys: string[] } |
oas-edit | Inline edit submitted (Enter / blur / operation column save), detail: { rowIndex, key, column, value }; in controlled mode the component does not write back data |
oas-edit-cancel | Inline edit cancelled (Esc / operation column cancel / empty submit restores), detail: { rowIndex, key, column, value } (value is the original value) |
oas-expand | Row expand/collapse (tree child rows or expandable content rows), detail: { key, expanded } |
oas-row-click | Row click (also toggles selection when not checkable), detail: { row, key } |
oas-scroll | Virtual scroll event (rAF throttled), detail: { scrollTop, start, end } |
oas-sort-change | Sort change, detail: { key, order: 'asc' | 'desc' | '' } |
Note:
columns.renderis a function type and can only be assigned via the property from JS — it cannot be expressed as a JSON string. Forfixedcolumns it is recommended to declarewidthexplicitly (sticky offsets fall back to 100px when omitted). Summary can also be written directly on a column assummary: 'sum' | 'avg' | 'count';children(tree child rows) andexpand(expandable row content) are both row data fields.
The loading placeholder row is exposed as ::part(loading-row), the summary row as ::part(summary-row), and the expandable content row as ::part(expand-row); each can be styled independently.