Skip to content
UI
v0.3.1

Search the docs

Table

Rows of data: sort, paging, a sticky header, empty and loading states.

Example

Latest rounds
Round
r_8f2c91Fish on!2.00 EUR18.40 EUR
r_4a7d10Cash Crop1.00 EUR0.00 EUR
r_c11e58Sands of Gold5.00 EUR1,250.00 EUR
r_09b3f2Midnight Hex2.00 EUR3.60 EUR
table.example.svelte
<script lang="ts">
  import { Table, useUiTranslator } from '@onlywin/ui-svelte';

  interface IRound {
    readonly bet: number;
    readonly game: string;
    readonly id: string;
    readonly win: number;
  }

  const rows: ReadonlyArray<IRound> = [
    { bet: 200, game: 'Fish on!', id: 'r_8f2c91', win: 1840 },
    { bet: 100, game: 'Cash Crop', id: 'r_4a7d10', win: 0 },
    { bet: 500, game: 'Sands of Gold', id: 'r_c11e58', win: 125_000 },
    { bet: 200, game: 'Midnight Hex', id: 'r_09b3f2', win: 360 },
    { bet: 100, game: 'Cat Story', id: 'r_7e66a4', win: 150 },
  ];
  const tr = useUiTranslator();
  // Money in minor units, formatted for the reader's language.
  const euro = (minor: number): string => tr().money(minor, 'EUR');
</script>

<Table
  caption="Latest rounds"
  captionVisible
  {rows}
  rowKey={(row) => row.id}
  pageSize={4}
  columns={[
    { key: 'id', label: 'Round', mono: true },
    { key: 'game', label: 'Game', sortable: true },
    {
      align: 'end',
      format: (row) => euro(row.bet),
      key: 'bet',
      label: 'Bet',
      sortable: true,
    },
    {
      align: 'end',
      format: (row) => euro(row.win),
      key: 'win',
      label: 'Win',
      sortable: true,
    },
  ]}
/>

Usage

Do

  • Sort on the client for small sets; pass onSortChange and total for server data.
  • Align money and counts to the end.

Don’t

  • Do not put forms in table cells.
  • Do not hide the caption; keep it for screen readers at least.

Accessibility

  • A real <table> with a caption; sorted columns carry aria-sort; sort buttons are named “Sort by …”.
  • Loading sets aria-busy and announces it; numbers align to the end and use tabular figures.

Keyboard

Table keyboard interactions
Keys Action
Enter, Space Activate
Tab, Shift+Tab Move focus in and out

Props

The same props in both frameworks. In React, class is className, events are camelCase (onClick), content passed as Svelte snippets is a ReactNode prop, every bindable value has a default… twin for uncontrolled use, and elementRef reaches the DOM element.

Table props
Prop Type Default Description
caption *required string — The table's name; visually hidden unless `captionVisible`.
captionVisible boolean false
cell Snippet<[TRow, IUiTableColumn<TRow>]> — A custom cell; default the column's formatted text.
columns *required ReadonlyArray<IUiTableColumn<TRow>> —
empty Snippet — Shown when there are no rows; default an empty state.
emptyTitle string —
loading boolean false
onPageChange (page: number) => void —
onSortChange (sort: IUiTableSort | null) => void — Server-side sorting: the table shows rows as given and reports clicks here.
page number 1 Bindable in Svelte; defaultPage in React.
pageSize number — Rows per page; without it the table does not page.
rowKey *required (row: TRow) => string —
rows *required ReadonlyArray<TRow> —
sort IUiTableSort | null null Bindable in Svelte; defaultSort in React.
sticky boolean true
total number — Server-side paging: the total row count, `rows` being the current page.