Skip to content

HyTable

Usage examples on this page are written for Lit / plain HTML (<hy-table>). The same component ships as HyTable in @whitespaceux/harmony-react (native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.

A semantic, lightweight data table for standard row/column display.

Rows are passed as rows: T[] against a columns: TableColumn<T>[] definition. The component is stateless with respect to data — sorting is controlled by the consumer via the sortKey + sortDirection props; the component only emits a sort event and updates the visual indicator / aria-sort when the consumer echoes the state back.

Row density is inherited from the surrounding context — wrap a region in data-density="compact" (or comfort) to shift cell padding without passing a prop per table. There is no local size override; tables are content-dense by design.

When a consumer outgrows hy-table (virtualization, selection, column resizing, multi-sort, pinning, grouping), they swap to hy-datagrid.

Examples

Sortable table — columns and rows are JS properties

javascript
<hy-table label="Team members"></hy-table>
<script>
const table = document.querySelector('hy-table');
table.columns = [
{ key: 'name', label: 'Name', sortable: true },
{ key: 'role', label: 'Role' },
{ key: 'commits', label: 'Commits', sortable: true, align: 'end' },
];
table.rows = [
{ name: 'Alice', role: 'Admin', commits: 128 },
{ name: 'Bob', role: 'Editor', commits: 74 },
{ name: 'Carol', role: 'Viewer', commits: 31 },
];
// Sorting is controlled: sort the data yourself, then echo the state back.
table.addEventListener('sort', (e) => {
const { key, direction } = e.detail;
table.sortKey = key;
table.sortDirection = direction;
table.rows = [...table.rows].sort((a, b) => {
if (direction === null) return 0;
const order = direction === 'asc' ? 1 : -1;
return a[key] < b[key] ? -order : a[key] > b[key] ? order : 0;
});
});
</script>

Presentation axes — emphasis, dividers, stripes, sticky header

html
<hy-table label="Inventory" emphasis="outlined" dividers="cells" striped sticky-header></hy-table>

Loading and empty states

html
<hy-table label="Users" loading></hy-table>
<hy-table label="Users" empty-text="No users yet"></hy-table>

API

Properties

PropertyAttributeTypeDefaultDescription
rowsT[][]Row data. Property-only — not an attribute (arrays can't be reflected).
columnsTableColumn<T>[][]Column definitions. Property-only.
emphasisemphasis'solid' | 'outlined' | 'tinted' | 'plain''solid'Visual chrome weight on the table surface.
dividersdividers'none' | 'rows' | 'cells''rows'Cell-divider treatment. Orthogonal to emphasis.
stripedstripedbooleanfalseZebra-stripe rows.
hoverablehoverablebooleantrueHighlight rows on hover.
stickyHeadersticky-headerbooleanfalseposition: sticky; top: 0 on the header row. The header sticks against the table's OWN scroll-container, so the table must be the vertical scroller: place it in a height-constrained container (a flex item with min-height: 0, or an explicit height) and it caps to that height and scrolls internally. A wrapper with overflow: auto around an unbounded table does NOT work — the table grows to its content and the wrapper scrolls instead, leaving the header with nothing to stick within.
stickyFirstColumnsticky-first-columnbooleanfalseposition: sticky; left: 0 on the first cell of each row.
captioncaptionstring''Renders a <caption>. When label is set separately the caption is visually hidden.
labellabelstring''aria-label on the table — required if caption is empty.
loadingloadingbooleanfalseRender skeleton rows instead of data.
sortKeysort-keystring | nullnullControlled sort column — component renders the indicator at sortKey.
sortDirectionsort-direction'asc' | 'desc'nullControlled sort direction.
emptyTextempty-textstring'No data'Fallback when rows.length === 0 and no empty slot is provided.

Events

EventDetailDescription
sortTableSortDetail — fired when a sortable header is activated.

Slots

SlotDescription
emptyCustom empty-state content (overrides emptyText).
loadingCustom loading placeholder (overrides the default skeletons).
header-{key}Override a specific column's header content. Replace {key} with the column key (e.g. header-email).
cell-{key}Override a specific column's cell content (static). Replace {key} with the column key (e.g. cell-status).

CSS Parts

PartDescription
baseOuter scroll-container wrapper.
tableThe <table> element.
head<thead>.
body<tbody>.
rowEach <tr> (both head and body).
header-cell<th>.
cell<td>.
sort-button<button> inside a sortable <th>.
sort-indicatorArrow icon beside the sort button.
caption<caption>.
emptyContainer for the empty-state slot/fallback.

CSS Custom Properties

PropertyDescription
--hy-table-border-colorHorizontal/vertical border colour.
--hy-table-header-backgroundHeader row background.
--hy-table-header-colorHeader row text colour.
--hy-table-row-hover-backgroundRow background on hover.
--hy-table-row-stripe-backgroundAlternate row background when striped.
--hy-table-cell-padding-xHorizontal cell padding.
--hy-table-cell-padding-yVertical cell padding.
--hy-table-radiusOuter container border-radius.
--hy-table-strokeBorder width.
--hy-table-hover-durationTransition duration for row hover state feedback
--hy-table-hover-easingTransition easing for row hover state feedback

Built with Lit. Documented with VitePress.