Table
The Table component provides a way to display tabular data. It’s a set of composable parts — Table.Root, Table.Header, Table.Body, Table.Footer, Table.Row, Table.Head, Table.Cell, and Table.Caption — that map onto the native HTML table elements, so you assemble exactly the structure you need. Use this when you lay out the rows and columns yourself; reach for Data Table when you want a data-driven table with built-in sorting, selection, and expansion.
Variations
Selection controls
Place a Checkbox or Radio in the first cell to make rows selectable, and mark the chosen rows with selected so they pick up the selected styling.
Row actions and pagination
Pass paginationProps to Table.Root to render a pagination bar below the table, and put a MoreActionsTooltip in a trailing cell for per-row actions.
Linked cells and rows
Give a Table.Cell a to to turn its content into a link, or give the whole Table.Row a to to make every cell navigate to the same place. Opt a cell out of a linked row with disableLink, and tune the link with linkProps (e.g. variant, suffixIcon).
Transparent variant
Set variant="transparent" to drop the outer border and header background — useful when embedding a table inside a card or another container.
Disabled rows
Set disabled on a Table.Row to dim it, show a not-allowed cursor, and suppress its click handler.
Installation
import { Table } from '@harnessio/ui/components'
//...
return ( <Table.Root size="normal"> <Table.Header> <Table.Row> <Table.Head>Name</Table.Head> <Table.Head>Email</Table.Head> <Table.Head>Display Name</Table.Head> <Table.Head>Date added</Table.Head> </Table.Row> </Table.Header> <Table.Body> <Table.Row> <Table.Cell>John Doe</Table.Cell> <Table.Cell>john@example.com</Table.Cell> <Table.Cell>John</Table.Cell> <Table.Cell>2023-01-01</Table.Cell> </Table.Row> </Table.Body> </Table.Root>)Anatomy
All parts of the Table are imported from the Table object and composed together. They map directly onto the native HTML table elements:
<Table.Root> <Table.Caption /> <Table.Header> <Table.Row> <Table.Head /> </Table.Row> </Table.Header> <Table.Body> <Table.Row> <Table.Cell /> </Table.Row> </Table.Body> <Table.Footer> <Table.Row> <Table.Cell /> </Table.Row> </Table.Footer></Table.Root>- Root — renders the
<table>inside a container and a scroll viewport that scrolls horizontally when the table overflows. Rows highlight on hover by default (turn that off withdisableHighlightOnHover). PassingpaginationPropsrenders a Pagination bar beneath the table. - Header / Body / Footer — render
<thead>,<tbody>, and<tfoot>. Group yourTable.Rows into these. - Row — a
<tr>. Mark itselectedfor selected styling ordisabledto dim and de-activate it. Give it ato/linkProps(or anonClick) to make the whole row navigate — each cell then renders the link, and only the first interactive cell stays in the tab order. - Head — a
<th>header cell. It can show atooltipPropstooltip (with a dashed underline) and a sort affordance viasortable/sortDirection. The firstHeadin a row automatically hides its leading divider. - Cell — a
<td>. Give it ato/linkPropsto render its content as a link, or anonClickto make it a button;disableLinkopts a cell out when its row is linked. - Caption — a
<caption>describing the table.
Best practices
Do
- Always give the table a header row of
Table.Headcells so every column is labeled. - Pick a
sizethat matches the density of the surrounding UI —compactfor dense data views,relaxedwhen the table is the focus of the page. - Keep a column’s content type consistent down the column (numbers with numbers, dates with dates) so rows stay scannable.
- When a whole row navigates somewhere, use a linked
Table.Rowso the entire row is the target, anddisableLinkon any cell that holds its own control.
Don’t
- Don’t put unrelated interactive controls inside a linked row or cell — the link covers the cell and will swallow the click.
- Don’t rely on the
selectedstyling alone to convey selection; pair it with a real Checkbox or Radio in the row. - Don’t use a table for layout — use it only for genuinely tabular data.
Content
- Write header labels as short nouns in sentence case (“Display name”, “Date added”).
- Keep cell content concise; move secondary detail into a tooltip or a dedicated column rather than crowding a cell.
- Use a consistent, sortable-friendly format for dates and numbers across the column.
Accessibility
Table renders semantic HTML table elements — <table>, <thead>, <tbody>, <tfoot>, <tr>, <th>, <td>, and <caption> — so assistive tech announces it as a table with rows and columns out of the box.
Keyboard
- Static tables aren’t interactive and don’t take focus. When a cell or row is a link (via
to/linkProps) or a button (viaonClick), that control is keyboard-focusable and activates with Enter (and Space for buttons). - For a linked row, only the first interactive cell is placed in the tab order (
tabIndex=0) while the rest are skipped (tabIndex=-1), so a row is a single tab stop rather than one per cell.
Labeling
- Column headers render as
<th>inside<thead>, andTable.Captionrenders a real<caption>— use it to give the table a name. - Selection controls in a row carry their own accessible name: give each Checkbox / Radio a
labeloraria-labelso it isn’t announced bare.
Focus & states
- Interactive cells and rows use the standard link/button focus ring. Selected rows are marked with a
data-checkedattribute and disabled rows withdata-disabled; disabled rows drop to a not-allowed cursor and have their click handler suppressed.
Known limitations
selectedis presentational. It only applies selected styling — it sets noaria-selected. The actual selected state must come from the Checkbox/Radio you place in the row.- Header cells set no explicit
scope.Table.Headrenders a plain<th>; for complex tables with row headers or spanning headers, addscope(orid/headers) yourself so the header-to-cell associations are unambiguous. - Sort state is visual only.
sortable/sortDirectionrender a sort icon but set noaria-sorton the header, so the current sort isn’t announced. Managearia-sortyourself if the table is sortable. - A disabled row isn’t disabled to assistive tech.
disabledsuppresses the row’s ownonClickand dims it, but doesn’t setaria-disabledon any controls inside the row — disable those controls directly if needed.
Properties
All parts extend their HTML equivalents, so the relevant native props (e.g. colSpan, width, aria-*) apply in addition to those below.
Root
The root component for the table. Renders the <table> inside a scroll container.
Prop | Required | Default | Type |
|---|---|---|---|
| size | false | 'normal' | 'normal' | 'relaxed' | 'compact' |
| variant | false | 'default' | 'default' | 'transparent' |
| className | false | string | |
| tableClassName | false | string | |
| disableHighlightOnHover | false | false | boolean |
| paginationProps | false | undefined | PaginationProps |
| stickyHeader | false | false | boolean |
| maxHeight | false | CSSProperties['maxHeight'] |
Header
The header section of the table (<thead>).
Prop | Required | Default | Type |
|---|---|---|---|
| className | false | string |
Body
The body section of the table (<tbody>).
Prop | Required | Default | Type |
|---|---|---|---|
| className | false | string |
Footer
The footer section of the table (<tfoot>).
Prop | Required | Default | Type |
|---|---|---|---|
| className | false | string |
Row
A row within the table (<tr>).
Prop | Required | Default | Type |
|---|---|---|---|
| className | false | string | |
| selected | false | boolean | |
| disabled | false | boolean | |
| onClick | false | (event: React.MouseEvent<HTMLTableRowElement>) => void | |
| to | false | string | |
| linkProps | false | Omit<LinkProps, 'to'> |
Head
A header cell within the table (<th>).
Prop | Required | Default | Type |
|---|---|---|---|
| className | false | string | |
| sortable | false | boolean | |
| sortDirection | false | 'asc' | 'desc' | false | |
| tooltipProps | false | Omit<TooltipProps, 'children'> | |
| containerProps | false | FlexProps | |
| hideDivider | false | false | boolean |
Cell
A cell within the table (<td>).
Prop | Required | Default | Type |
|---|---|---|---|
| className | false | string | |
| onClick | false | () => void | |
| to | false | string | |
| linkProps | false | Omit<LinkProps, 'to'> | |
| disableLink | false | false | boolean |
Caption
A caption for the table (<caption>).
Prop | Required | Default | Type |
|---|---|---|---|
| className | false | string |