Skip to content

Table

Accessible data table with sorting, row selection, expandable rows, column truncation, skeleton loading, and integrated pagination.

Data Display·Available inReactVueAngular·View as Markdown

Preview

tsx
NameRoleStatus
Ada LovelaceAdmin
active
Linus TorvaldsEngineer
invited
Grace HopperEngineer
active
Alan TuringAdmin
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/react

Then import the bundled stylesheet once at app entry: import "@sisyphos-ui/react/styles.css";

Usage

Idiomatic usage in each supported framework
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`.

tsx
NameRoleStatus
Ada LovelaceAdminactive
Alan TuringAdminpaused
Grace HopperEngineeractive
Linus TorvaldsEngineerinvited

Row selection

`selectable` shows a checkbox column. Controlled via `selectedIds` + `onSelectionChange`.

tsx
Selected: none
NameRoleStatus
Ada LovelaceAdminactive
Linus TorvaldsEngineerinvited
Grace HopperEngineeractive
Alan TuringAdminpaused

Dense & striped

`size="sm"` + `striped` + `hoverable` + `bordered`.

tsx
NameRoleStatus
Ada LovelaceAdminactive
Linus TorvaldsEngineerinvited
Grace HopperEngineeractive
Alan TuringAdminpaused

Loading skeleton

`loading` renders `skeletonRows` placeholder rows instead of data.

tsx
NameRoleStatus

Empty state

Pass any ReactNode to `empty` — ideal spot for a primary call-to-action.

tsx
NameRoleStatus
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.

tsx
NameRoleStatus
Ada 1Admin
active
Linus 2Engineer
invited
Grace 3Engineer
paused
Alan 4Admin
active
Rich 5Engineer
invited
Margaret 6Engineer
paused
Dennis 7Admin
active
Ken 8Engineer
invited
Barbara 9Engineer
paused
Donald 10Admin
active
Showing 110 of 48

Filter rows externally and pass the result as `data`. A standalone `Input` + `empty` handles the zero-state message.

tsx
NameRoleStatus
Ada 1Adminactive
Linus 2Engineerinvited
Grace 3Engineerpaused
Alan 4Adminactive
Rich 5Engineerinvited
Margaret 6Engineerpaused
Dennis 7Adminactive
Ken 8Engineerinvited

Row detail panel

`onRowClick` toggles an active row; render the detail panel anywhere in your layout — no built-in expandable API required.

tsx
NameRoleStatus
Ada LovelaceAdminactive
Linus TorvaldsEngineerinvited
Grace HopperEngineeractive
Alan TuringAdminpaused
Ada Lovelace — row detail
User ID: 1
Record: {"id":1,"name":"Ada Lovelace","role":"Admin","status":"active"}
Click the row again to close this detail panel.

Real-world composition

Search + sort + select + pagination in one table — the patterns compose cleanly without a bespoke table toolbar API.

tsx
48 teammates
NameRoleStatus
Ada 1Admin
active
Ada 11Engineer
invited
Ada 21Engineer
paused
Ada 31Admin
active
Ada 41Engineer
invited
Page 1 of 10

API

PropTypeDefaultDescription
data*T[]Row data.
columns*TableColumn<T>[]Column definitions.
rowKey(row, index) => string | numberReturns a unique id for a row. Required for selection and expansion.
loadingbooleanfalseRenders skeleton rows in place of data.
loadingDelaynumber0Ms to wait before showing the skeleton — smooths flicker on fast loads.
skeletonRowsnumber5Skeleton row count while loading.
emptyReactNode"No data"Content shown when `data` is empty.
selectablebooleanfalseAdds a checkbox column for row selection.
selectedIds(string | number)[]Controlled selection ids.
onSelectionChange(ids) => voidCalled when the selection set changes.
rowSelectionMode"checkbox" | "click" | "doubleClick""checkbox"How a row toggles its selected state.
sortSortState | nullControlled sort state ({ key, direction }).
onSortChange(sort) => voidCalled as sortable headers cycle asc → desc → cleared.
actions(row, index) => ReactNodeRenders the rightmost actions cell per row.
onRowClick(row, index) => voidFires on row click (skips checkbox / actions cells).
onRowDoubleClick(row, index) => voidFires on row double-click — independent of selection.
onRowContextMenu(event, row, index) => voidFires on row right-click; useful for wiring a context menu.
rowClassName(row, index) => string | undefinedReturns an extra class for state-driven row highlighting.
expandablebooleanfalseAdds an expand chevron column with detail rows.
renderExpanded(row, index) => ReactNodeRenderer for the expanded detail row.
searchablebooleanfalseRenders the built-in search input inside the toolbar.
filtersTableFilterField[]Declarative filter controls rendered as a second toolbar row.
paginationTablePaginationConfigRenders a `<Pagination>` footer wired to the supplied config.
heightMode"auto" | "flex" | "content""auto"Whether the body fits content, fills remaining space, or scrolls.
stickyHeaderbooleanfalsePin the header row when scrolling a constrained container.
size"sm" | "md" | "lg""md"Density variant.
stripedbooleanfalseAlternating-row background.
hoverablebooleantrueApply hover background on rows.
borderedbooleanfalseOuter border + cell borders.

The full API including refs, ARIA attributes, and HTML passthroughs lives in the package README on npm.

Accessibility

  • <thead> cells use aria-sort="ascending" | "descending" | "none" when sortable.
  • Selected rows expose aria-selected="true".
  • Header checkbox toggles tristate via the DOM indeterminate flag when only some rows are selected.
  • Skeleton rows are decorative — they are not announced as data rows.

Keyboard interactions

KeyAction
EnterthenSpaceActivates the focused checkbox or expand button.
TabMoves focus through interactive cells (sortable headers, checkboxes, action buttons).
Need more?View on npm →
Was this page helpful?