Components / data-display
Table
Displays records in rows and columns for scanning and comparison.
import { Table } from '@basiiik/ui';
The snippet an agent gets
From get_component_snippet, with the required props filled in from the spec. Every snippet is type-checked against the library before it is shipped.
import { Table, TableBody, TableCell, TableRow } from '@basiiik/ui';
<Table><TableBody><TableRow><TableCell>Invoice 042</TableCell></TableRow></TableBody></Table>- Compose Table, TableHeader, TableBody, TableRow, TableHeaderCell, TableCell.
densityon Table flows to every cell. - Compose with: avatar, badge, table-row.
- Storybook: Data display/Table.
Props
| Prop | Type | Notes |
|---|---|---|
density | "compact" | "comfortable" | Default "comfortable" |
maxHeight | string | number | |
children | ReactNode |
Other props
| Prop | Type | What it does |
|---|---|---|
caption | ReactNode | Visible description shown above the table. Also names it. Without it, pass aria-label. |
States. Compose Table, TableHeader, TableBody, TableRow, TableHeaderCell, TableCell. density on Table flows to every cell.
Native attributes such as className, id and data-* are forwarded to the root element.
Parts
TableHeader
Wraps the header row (<thead>). A TableRow inside it becomes a header row by itself, so it needs no type.
<TableHeader><TableRow><TableHeaderCell>Name</TableHeaderCell></TableRow></TableHeader>TableBody
Wraps the body rows (<tbody>).
<TableBody><TableRow><TableCell>Ada</TableCell></TableRow></TableBody>TableHeaderCell
A column header. Give it sort (the current direction: ascending, descending or none) and onSort to make it a sortable button that sets aria-sort. align="end" right-aligns a numeric column. hideBelow="md" hides the column when the table's container is narrower than 640px (sm 480, lg 800); pin="end" keeps it in view while the table scrolls sideways. Set both on the header cell and on every TableCell of that column.
<TableHeaderCell sort="ascending" onSort={() => undefined}>Name</TableHeaderCell>TableHeaderCell props
| Prop | Type | What it does |
|---|---|---|
sort | "none" | "ascending" | "descending" | Current sort direction. Sets aria-sort and, with onSort, makes the header a button. |
onSort | () => void | |
align | "start" | "end" | |
hideBelow | "sm" | "md" | "lg" | Hides this column when the table's container is narrower than sm 480px, md 640px or lg 800px. Set it on the header cell and on every cell of the column. |
pin | "start" | "end" | Keeps this column in view at the start or the end while the table scrolls sideways. Set it on the header cell and on every cell of the column. |
TableCell
A body cell. align="end" right-aligns numbers. Repeat the column's hideBelow and pin from its TableHeaderCell on every cell.
<TableCell align="end" hideBelow="md">42</TableCell>TableCell props
| Prop | Type | What it does |
|---|---|---|
align | "start" | "end" | Right-align numbers. |
hideBelow | "sm" | "md" | "lg" | Same as on TableHeaderCell: set it on every cell of the column. |
pin | "start" | "end" | Same as on TableHeaderCell: set it on every cell of the column. |
Use it when
- Compare structured records across several attributes
- A long list of records in a bounded area, with a header that stays in view (maxHeight)
- Data that must work in a narrow panel or on a phone: hide the less important columns and keep the row's name
Do not use it when
- Layout
- Simple one-attribute lists (use List Item)
- A phone-first list where each record needs several lines (use List Item or Card in a stack)
Rules for agents
- Compose Table > TableHeader and TableBody > TableRow > TableHeaderCell or TableCell
- Name every Table: a visible caption, or aria-label when there is none
- Right-align numbers with align="end" on both the TableHeaderCell and its TableCells
- For sortable columns, pass sort and onSort on the TableHeaderCell; only one column is sorted at a time
- Use density="compact" for dense admin views; comfortable is the default
- Show an Empty State in place of the rows when there is no data, and Pagination below long tables
- On narrow containers hide the less important columns with hideBelow="sm" (below 480px), "md" (below 640px) or "lg" (below 800px) on the TableHeaderCell and on every TableCell of that column; keep the column that identifies the row (usually the first) and the actions always visible
- Do not hide a column that holds the only way to do something (a status badge people filter by, an action); move it or pin it instead
- Keep a column in view while the table scrolls sideways with pin="start" (the name) or pin="end" (row actions) on the header cell and on every cell of that column; pin at most one column on each side
- For a long table inside a page section set maxHeight (a number of px, or a CSS length): the body scrolls and the header row stays in view; leave it off when the page itself scrolls
- Put row actions in one last column with pin="end", a visually hidden header ("Actions") and a single Dropdown Menu or icon button per row
- Do not wrap the table in your own overflow-x container: it already has one
Avoid
- Using a Table for layout
- A Table for a list with a single attribute (use List Item)
- Header cells that are not TableHeaderCell, so columns have no headers for screen readers
- Sorting by clicking a column without showing the sort direction
- Putting several actions as text links in every row instead of one Dropdown Menu
- Hiding columns with your own media queries or display:none (use hideBelow, which follows the table's container)
- hideBelow on the header cell but not on the cells of the column (or the reverse): the columns no longer line up
- Pinning more than one column on a side, or a wide column (a pinned column should be narrow)
- A Table with maxHeight and no caption or aria-label: the scrolling region has no name
Writing the content
- Column headers are short nouns: 'Name', 'Status', 'Amount'
- Use the same unit and format down a column, and put the unit in the header
- Use sentence case
Keyboard
- Interactive cells and row actions follow Tab order
- A table that scrolls is one extra Tab stop (the scroll region); arrow keys, Page Up/Down and Home/End then scroll it
Accessibility
- Use <th scope> for headers
- aria-sort on sortable column headers
- Provide a caption or aria-label
- A table that overflows (sideways, or taller than maxHeight) becomes a region named by its caption or aria-label and takes focus, so a keyboard can scroll it; one that fits is not in the tab order
- A column hidden with hideBelow is removed from the accessibility tree too: do not hide the column that identifies the row
Colour tokens it reads
color.bg.brand.subtlecolor.bg.mutedcolor.bg.subtlecolor.bg.success.subtlecolor.bg.surfacecolor.border.defaultcolor.border.subtlecolor.text.brandcolor.text.primarycolor.text.secondarycolor.text.successRelated
data-display 2 Figma variants Try it in the live demo