Table
Accessible data table with sorting, row selection, expandable rows, column truncation, skeleton loading, and integrated pagination.
Preview
| Name | Role | Status |
|---|---|---|
| Ada Lovelace | Admin | active |
| Linus Torvalds | Engineer | invited |
| Grace Hopper | Engineer | active |
| Alan Turing | Admin | paused |
Switch the framework picker (top-right of the panel) to render the same demo live in React, Vue, or Angular — same class names, ARIA, and visual output across all three.
Installation
Sisyphos UI ships unified packages for React, Vue, and Angular. Pick the one that matches your stack — every framework exports the same component classes, ARIA semantics, and CSS tokens.
$ pnpm add @sisyphos-ui/reactThen import the bundled stylesheet once at app entry: import "@sisyphos-ui/react/styles.css";
Usage
import { Table } from "@sisyphos-ui/react";
import type { TableColumn } from "@sisyphos-ui/react";
interface User { id: number; name: string; role: string; }
const data: User[] = [
{ id: 1, name: "Ada Lovelace", role: "Mathematician" },
{ id: 2, name: "Grace Hopper", role: "Compiler pioneer" },
];
const columns: TableColumn<User>[] = [
{ id: "name", header: "Name", accessor: "name", sortable: true },
{ id: "role", header: "Role", accessor: "role" },
];
export function People() {
return <Table data={data} columns={columns} rowKey={(r) => r.id} striped />;
}Sortable
Mark columns `sortable`, then control the sort state with `sort` + `onSortChange`.
| Name | Role | Status |
|---|---|---|
| Ada Lovelace | Admin | active |
| Alan Turing | Admin | paused |
| Grace Hopper | Engineer | active |
| Linus Torvalds | Engineer | invited |
Row selection
`selectable` shows a checkbox column. Controlled via `selectedIds` + `onSelectionChange`.
| Name | Role | Status | |
|---|---|---|---|
| Ada Lovelace | Admin | active | |
| Linus Torvalds | Engineer | invited | |
| Grace Hopper | Engineer | active | |
| Alan Turing | Admin | paused |
Dense & striped
`size="sm"` + `striped` + `hoverable` + `bordered`.
| Name | Role | Status |
|---|---|---|
| Ada Lovelace | Admin | active |
| Linus Torvalds | Engineer | invited |
| Grace Hopper | Engineer | active |
| Alan Turing | Admin | paused |
Loading skeleton
`loading` renders `skeletonRows` placeholder rows instead of data.
| Name | Role | Status |
|---|---|---|
Empty state
Pass any ReactNode to `empty` — ideal spot for a primary call-to-action.
| Name | Role | Status |
|---|---|---|
No users yet. . | ||
With pagination
Slice your data by page, then render the companion `Pagination` component below the table. Add your own row-count summary alongside.
| Name | Role | Status |
|---|---|---|
| Ada 1 | Admin | active |
| Linus 2 | Engineer | invited |
| Grace 3 | Engineer | paused |
| Alan 4 | Admin | active |
| Rich 5 | Engineer | invited |
| Margaret 6 | Engineer | paused |
| Dennis 7 | Admin | active |
| Ken 8 | Engineer | invited |
| Barbara 9 | Engineer | paused |
| Donald 10 | Admin | active |
With search
Filter rows externally and pass the result as `data`. A standalone `Input` + `empty` handles the zero-state message.
| Name | Role | Status |
|---|---|---|
| Ada 1 | Admin | active |
| Linus 2 | Engineer | invited |
| Grace 3 | Engineer | paused |
| Alan 4 | Admin | active |
| Rich 5 | Engineer | invited |
| Margaret 6 | Engineer | paused |
| Dennis 7 | Admin | active |
| Ken 8 | Engineer | invited |
Row detail panel
`onRowClick` toggles an active row; render the detail panel anywhere in your layout — no built-in expandable API required.
| Name | Role | Status |
|---|---|---|
| Ada Lovelace | Admin | active |
| Linus Torvalds | Engineer | invited |
| Grace Hopper | Engineer | active |
| Alan Turing | Admin | paused |
{"id":1,"name":"Ada Lovelace","role":"Admin","status":"active"}Real-world composition
Search + sort + select + pagination in one table — the patterns compose cleanly without a bespoke table toolbar API.
| Name | Role | Status | |
|---|---|---|---|
| Ada 1 | Admin | active | |
| Ada 11 | Engineer | invited | |
| Ada 21 | Engineer | paused | |
| Ada 31 | Admin | active | |
| Ada 41 | Engineer | invited |
API
| Prop | Type | Default | Description |
|---|---|---|---|
| data* | T[] | — | Row data. |
| columns* | TableColumn<T>[] | — | Column definitions. |
| rowKey | (row, index) => string | number | — | Returns a unique id for a row. Required for selection and expansion. |
| loading | boolean | false | Renders skeleton rows in place of data. |
| loadingDelay | number | 0 | Ms to wait before showing the skeleton — smooths flicker on fast loads. |
| skeletonRows | number | 5 | Skeleton row count while loading. |
| empty | ReactNode | "No data" | Content shown when `data` is empty. |
| selectable | boolean | false | Adds a checkbox column for row selection. |
| selectedIds | (string | number)[] | — | Controlled selection ids. |
| onSelectionChange | (ids) => void | — | Called when the selection set changes. |
| rowSelectionMode | "checkbox" | "click" | "doubleClick" | "checkbox" | How a row toggles its selected state. |
| sort | SortState | null | — | Controlled sort state ({ key, direction }). |
| onSortChange | (sort) => void | — | Called as sortable headers cycle asc → desc → cleared. |
| actions | (row, index) => ReactNode | — | Renders the rightmost actions cell per row. |
| onRowClick | (row, index) => void | — | Fires on row click (skips checkbox / actions cells). |
| onRowDoubleClick | (row, index) => void | — | Fires on row double-click — independent of selection. |
| onRowContextMenu | (event, row, index) => void | — | Fires on row right-click; useful for wiring a context menu. |
| rowClassName | (row, index) => string | undefined | — | Returns an extra class for state-driven row highlighting. |
| expandable | boolean | false | Adds an expand chevron column with detail rows. |
| renderExpanded | (row, index) => ReactNode | — | Renderer for the expanded detail row. |
| searchable | boolean | false | Renders the built-in search input inside the toolbar. |
| filters | TableFilterField[] | — | Declarative filter controls rendered as a second toolbar row. |
| pagination | TablePaginationConfig | — | Renders a `<Pagination>` footer wired to the supplied config. |
| heightMode | "auto" | "flex" | "content" | "auto" | Whether the body fits content, fills remaining space, or scrolls. |
| stickyHeader | boolean | false | Pin the header row when scrolling a constrained container. |
| size | "sm" | "md" | "lg" | "md" | Density variant. |
| striped | boolean | false | Alternating-row background. |
| hoverable | boolean | true | Apply hover background on rows. |
| bordered | boolean | false | Outer border + cell borders. |
The full API including refs, ARIA attributes, and HTML passthroughs lives in the package README on npm.
Accessibility
<thead>cells usearia-sort="ascending" | "descending" | "none"when sortable.- Selected rows expose
aria-selected="true". - Header checkbox toggles tristate via the DOM
indeterminateflag when only some rows are selected. - Skeleton rows are decorative — they are not announced as data rows.
Keyboard interactions
| Key | Action |
|---|---|
| EnterthenSpace | Activates the focused checkbox or expand button. |
| Tab | Moves focus through interactive cells (sortable headers, checkboxes, action buttons). |