# Table
Table renders typed records with captions, sorting, controlled selection, responsive layouts, loading, empty, and row-action states.
Use Table for records compared across consistent fields. Add a caption. Choose cards for reordered narrow layouts or scroll to preserve columns. Controlled selection requires `rowKey` and localized selection labels.
Purpose
Use Table for records that users compare across consistent fields. Use a list when records do not share a comparison structure.
Anatomy
Columns define headers, alignment, sorting, and cell behavior. Rows supply typed data. Optional snippets render cells and row actions.
Add a caption that identifies the record set. Set `captionVisuallyHidden` when the surrounding page already shows the same title.
Use descriptive column labels and preserve the sort direction on sortable headers. Keep sorting state in the parent when a server owns the result order. When rows are reconstructed after sorting or fetching, pass `rowKey={(row) => row.id}` so cells retain their identity.
The default `responsive="cards"` layout stacks labelled values on narrow screens. Use `responsive="scroll"` when preserving the column comparison is more important than removing horizontal scrolling.
Controlled selection requires `rowKey`, a `selectedKeys` set, and `onSelectedKeysChange`. Supply localized `selectAllLabel` and `selectRowLabel` text. The select-all control reports a mixed state when only some available rows are selected. Disabled rows do not change through row or select-all controls.
Give each row action a label that includes the record name when nearby text does not provide it. Table does not paginate or virtualize records.
Tokens and style hooks
Table uses card, chrome, control, and selected layers with table geometry, text, border, radius, focus, and motion tokens. Cell snippets should use the same semantic variables.
## API
The package declarations define these public members.
| Member | Type | Kind | Required | Default | Description |
| --- | --- | --- | --- | --- | --- |
| `caption` | `string` | prop | No | `undefined` | Table caption. |
| `captionVisuallyHidden` | `boolean` | prop | No | `false` | Visually hides the caption while keeping it available to assistive technology. |
| `cell` | `Snippet<[TableCellArgs]>` | snippet | No | `undefined` | Custom cell render. Falls back to `String(row[column.key])` when omitted. |
| `columns` | `TableColumn[]` | prop | Yes | - | Column definitions, left to right. A trailing action cell is appended when `rowAction` is set. |
| `empty` | `Snippet` | snippet | No | `undefined` | Fallback when `rows` is empty and not loading. Defaults to a simple `EmptyState`. |
| `loading` | `boolean` | prop | No | `false` | Replace the body with shimmering skeleton rows. |
| `loadingRows` | `number` | prop | No | `5` | Skeleton row count while `loading`. |
| `onSortByChange` | `(sort: TableSort) => void` | callback | No | `undefined` | Fired when a sortable header is clicked. Receives the newly-resolved sort state, so consumers can sort without binding `sortBy`. |
| `responsive` | `"cards" \| "scroll"` | prop | No | `'cards'` | Narrow-screen layout. Defaults to `cards`. |
| `rowAction` | `Snippet<[TableRowArgs]>` | snippet | No | `undefined` | Trailing per-row action cell, e.g. a `Menu` trigger. |
| `rowKey` | `TableRowKey` | prop | No | `undefined` | Stable primitive identity for reconstructed rows. Defaults to the row object's identity. |
| `rows` | `Row[]` | prop | Yes | - | Row records. Each cell reads `row[column.key]` unless `cell` overrides it. |
| `selection` | `TableSelection \| undefined` | prop | No | `undefined` | Controlled row-selection state. |
| `sortBy` | `TableSort` | binding | No | `undefined` | Active sort — bindable. Drives the header chevron. |
## Related
- [Eyebrow](/docs/components/eyebrow): Tracked label used above headings.
- [Avatar](/docs/components/avatar): Identity image or fallback with an optional status marker.
- [CodeBlock](/docs/components/code-block): Read-only code surface with copy support.