Table

Alpha

Accessible data table with automatic ARIA wiring for sorting and row selection. Renders native semantic HTML — no ARIA props required from consumers.

Import

import { Table } from "@compa11y/react";

Usage

Example.tsx
import { Table } from "@compa11y/react";

// Basic read-only table
function BasicExample() {
  return (
    <Table caption="Team members">
      <Table.Head>
        <Table.Row>
          <Table.Header>Name</Table.Header>
          <Table.Header>Role</Table.Header>
        </Table.Row>
      </Table.Head>
      <Table.Body>
        <Table.Row>
          <Table.Cell>Alice</Table.Cell>
          <Table.Cell>Engineer</Table.Cell>
        </Table.Row>
      </Table.Body>
    </Table>
  );
}

// Sortable table
function SortableExample() {
  const [sortKey, setSortKey] = useState<string | null>(null);
  const [sortDir, setSortDir] = useState<"none" | "ascending" | "descending">("none");

  return (
    <Table
      caption="Products"
      sortKey={sortKey}
      sortDirection={sortDir}
      onSortChange={(key, dir) => { setSortKey(key); setSortDir(dir); }}
    >
      <Table.Head>
        <Table.Row>
          <Table.Header sortKey="name">Name</Table.Header>
          <Table.Header sortKey="price">Price</Table.Header>
        </Table.Row>
      </Table.Head>
      <Table.Body>
        <Table.Row>
          <Table.Cell>Widget</Table.Cell>
          <Table.Cell>$9.99</Table.Cell>
        </Table.Row>
      </Table.Body>
    </Table>
  );
}

// Selectable table
function SelectableExample() {
  const [selected, setSelected] = useState<string[]>([]);
  const users = [{ id: "1", name: "Alice" }, { id: "2", name: "Bob" }];

  return (
    <Table caption="Users" selectedRows={selected} onSelectionChange={setSelected}>
      <Table.Head>
        <Table.Row>
          <Table.SelectAllCell rowIds={users.map(u => u.id)} />
          <Table.Header>Name</Table.Header>
        </Table.Row>
      </Table.Head>
      <Table.Body>
        {users.map(u => (
          <Table.Row key={u.id} rowId={u.id}>
            <Table.SelectCell label={`Select ${u.name}`} />
            <Table.Cell>{u.name}</Table.Cell>
          </Table.Row>
        ))}
      </Table.Body>
    </Table>
  );
}

Features

  • Automatic <caption> rendering from caption prop
  • scope='col' and scope='row' applied automatically to header cells
  • aria-sort on sortable columns; cycles none → ascending → descending → none
  • Sort button uses native <button> — no div click handlers
  • aria-selected on selectable rows
  • Select-all checkbox with indeterminate state
  • aria-busy applied when isLoading is true
  • Focus retained on sort button after re-render
  • Screen reader announcements for sort and selection changes
  • Dev warnings for missing caption/label and missing SelectCell label

Props

Table component props
PropTypeDefaultDescription
captionstring-Renders <caption>; required for accessible name unless aria-label or aria-labelledby is used
captionHiddenbooleanfalseVisually hides the caption while keeping it accessible
sortKeystring | null-Controlled sort column key
sortDirection'none' | 'ascending' | 'descending''none'Controlled sort direction
onSortChange(key: string, direction: SortDirection) => void-Called when sort column or direction changes
selectedRowsstring[]-Controlled selected row IDs
defaultSelectedRowsstring[]-Uncontrolled default selected rows
onSelectionChange(rows: string[]) => void-Called when row selection changes
isLoadingbooleanfalseSets aria-busy on the table and shows loading state

Sub-components

Table.Head

Table.Head props
PropTypeDefaultDescription
children*ReactNode-Header rows

Table.Body

Table.Body props
PropTypeDefaultDescription
children*ReactNode-Body rows

Table.Row

Table.Row props
PropTypeDefaultDescription
rowIdstring-Required for selection — sets aria-selected on the row
children*ReactNode-Cells

Table.Header

Table.Header props
PropTypeDefaultDescription
sortKeystring-Enables a sort button on this column
scopestring-Override the auto-detected scope attribute
children*ReactNode-Header cell content

Table.Cell

Table.Cell props
PropTypeDefaultDescription
children*ReactNode-Cell content

Table.SelectAllCell

Table.SelectAllCell props
PropTypeDefaultDescription
rowIds*string[]-All selectable row IDs — drives the indeterminate state

Table.SelectCell

Table.SelectCell props
PropTypeDefaultDescription
label*string-Accessible label for the row checkbox e.g. 'Select Alice'

Table.EmptyState

Table.EmptyState props
PropTypeDefaultDescription
colSpan*number-Spans the full table width
children*ReactNode-Empty state message

Table.LoadingState

Table.LoadingState props
PropTypeDefaultDescription
colSpan*number-Spans the full table width
children*ReactNode-Loading message

Keyboard Interactions

Accessibility
All keyboard interactions follow WAI-ARIA best practices and work without any additional configuration.
Keyboard shortcuts for Table
KeyAction
TabMoves between sort buttons and row checkboxes
Enter / SpaceCycles sort direction on a sort button
SpaceToggles row selection on a row checkbox