Table
A responsive table with a surface style, wrapping or compact cells, sticky headers, pinned columns and scroll hints.
| Invoice | Client | Status | Amount |
|---|---|---|---|
| INV-0418 | Northwind | $2,400.00 | |
| INV-0419 | Globex | $860.00 | |
| INV-0420 | Initech | $1,320.50 | |
| INV-0421 | Umbrella | $415.00 | |
| Total | $4,995.50 | ||
pnpm dlx shadcn@latest add https://hextaui.com/r/table.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add class-variance-authority cnCopy and paste the following code into your project.
components/ui/table.tsx Update the import paths to match your project setup.
Use <TableHead scope="row"> for the cell that names each row. It reads as the row's header to screen readers and gets a stronger text color. For sorting, filtering and selection, build on <DataTable />.
Default
The default variant has no surface, just hairlines between rows, for tables that sit inside a card or a page section. <TableCaption /> describes the table below it.
| Version | Released | Changes |
|---|---|---|
| 2.4.0 | Oct 2, 2026 | 18 |
| 2.3.1 | Sep 21, 2026 | 4 |
| 2.3.0 | Sep 12, 2026 | 23 |
Compact
size="sm" tightens row height and padding for dense data.
| Region | Latency | Uptime |
|---|---|---|
| us-east-1 | 42 ms | 99.99% |
| eu-west-2 | 38 ms | 99.98% |
| ap-south-1 | 71 ms | 99.95% |
Wrapping text
Cells stay on one line by default, which suits data. wrap lets them wrap and aligns rows to the top, for prose like glossaries or API references. Pair it with table-fixed and column widths.
| Term | Meaning |
|---|---|
| Concentric radius | An inner corner radius equal to the outer radius minus the gap between the two edges, so nested shapes look like they belong together. |
| Hairline | The thinnest line the screen can draw: 1px on standard displays and two thirds of a pixel on high-density ones. |
Wide tables
Tables wider than their container scroll sideways. Soft fades show there's more on either side, and the scroll area becomes focusable so keyboard users can scroll it with the arrow keys. Turn the fades off with scrollFade={false}.
| Metric | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Visitors | 812 | 904 | 1,021 | 990 | 1,203 | 1,340 | 1,288 | 1,410 | 1,502 | 1,622 | 1,590 | 1,711 |
| Sign-ups | 41 | 52 | 66 | 59 | 71 | 83 | 80 | 92 | 97 | 110 | 104 | 121 |
Pinned column
pinned="start" keeps the first column in view while the rest scrolls, and pinnedEdge shows a shadow once content slides under it.
| Team | Q1 2025 | Q2 2025 | Q3 2025 | Q4 2025 | Q1 2026 | Q2 2026 | Q3 2026 |
|---|---|---|---|---|---|---|---|
| Design systems | 12 | 14 | 15 | 18 | 21 | 22 | 24 |
| Platform | 31 | 30 | 33 | 35 | 34 | 38 | 40 |
| Growth | 8 | 9 | 11 | 10 | 12 | 15 | 16 |
Sticky header
stickyHeader keeps the header in view in a height-limited table, and adds a hairline under it once rows scroll beneath.
| Event | Type | When |
|---|---|---|
| evt_400 | deploy | 2 min ago |
| evt_401 | rollback | 9 min ago |
| evt_402 | scale | 16 min ago |
| evt_403 | restart | 23 min ago |
| evt_404 | deploy | 30 min ago |
| evt_405 | rollback | 37 min ago |
| evt_406 | scale | 44 min ago |
| evt_407 | restart | 51 min ago |
| evt_408 | deploy | 58 min ago |
| evt_409 | rollback | 65 min ago |
| evt_40a | scale | 72 min ago |
| evt_40b | restart | 79 min ago |
| evt_40c | deploy | 86 min ago |
| evt_40d | rollback | 93 min ago |
| evt_40e | scale | 100 min ago |
| evt_40f | restart | 107 min ago |
| evt_410 | deploy | 114 min ago |
| evt_411 | rollback | 121 min ago |
| evt_412 | scale | 128 min ago |
| evt_413 | restart | 135 min ago |
| evt_414 | deploy | 142 min ago |
| evt_415 | rollback | 149 min ago |
| evt_416 | scale | 156 min ago |
| evt_417 | restart | 163 min ago |
Right to left
Alignment, pinned columns and scroll fades follow the reading direction.
| الفريق | الأعضاء | الميزانية |
|---|---|---|
| التصميم | 8 | ١٢٬٠٠٠ |
| الهندسة | 21 | ٤٨٬٥٠٠ |
| Key | Action |
|---|---|
| Tab | Focuses the scroll area when the table overflows, then the links and buttons inside it. |
| ←→ | Scrolls a focused, overflowing table sideways. |
| ↑↓ | Scrolls a focused table with a sticky header up and down. |
- The parts render native table elements, so screen readers announce rows, columns and headers without extra roles.
- Mark the cell that names a row with
<TableHead scope="row">so each value is read with its row and column. - The scroll area only joins the tab order while it overflows, so tables that fit don't add an extra stop.
Every part renders its native element and accepts its attributes.
| Prop | Type | Default |
|---|---|---|
variantsurface adds a rounded hairline frame and a muted header band. | "default" | "surface" | "default" |
size | "sm" | "default" | "default" |
wrapLet cell text wrap and align rows to the top. | boolean | false |
scrollFadeFade the edges that have more content to scroll to. Skipped when columns are pinned. | boolean | true |
stickyHeaderKeep the header in view. Give the container a max height through containerClassName. | boolean | false |
containerClassNameClasses for the scroll container. | string | – |
containerRefRef to the scroll container. | Ref<HTMLDivElement> | – |
| Attribute | Description |
|---|---|
data-slot="table-frame" | The outer frame, with data-variant, data-size and data-wrap. |
data-slot="table-container" | The scroll container. |
data-overflowing | Present on the container while its content overflows. It's focusable then. |
data-scrolled-start / data-scrolled-end | Present while there's content hidden before or after the visible area. |
data-scrolled-top | Present once a sticky-header table has scrolled. |
--table-bg | Row background. Follows a surrounding card or popover. |
--table-head-bg | Header band background in the surface variant. |
--table-cell-px / --table-cell-py | Cell padding. Set by size. |
| Prop | Type | Default |
|---|---|---|
scopeUse row for the cell that names a row. Inside the body it gets cell padding and foreground text. | "col" | "row" | – |
alignend also switches to tabular numbers so digits line up. | "start" | "center" | "end" | "start" |
pinnedKeep the column in view while the table scrolls sideways. Set the same value on its header and every cell. | "start" | "end" | – |
pinnedEdgeDraw a soft shadow on the inner edge of the last pinned column while content is hidden behind it. | boolean | false |
| Attribute | Description |
|---|---|
data-slot="table-head" | Target header cells in CSS. |
data-align | The alignment. |
data-pinned | The pinned side. |
| Prop | Type | Default |
|---|---|---|
alignend also switches to tabular numbers so digits line up. | "start" | "center" | "end" | "start" |
pinnedKeep the column in view while the table scrolls sideways. Set the same value on its header and every cell. | "start" | "end" | – |
pinnedEdgeDraw a soft shadow on the inner edge of the last pinned column while content is hidden behind it. | boolean | false |
| Attribute | Description |
|---|---|
data-slot="table-cell" | Target cells in CSS. |
data-align | The alignment. |
data-pinned | The pinned side. |
| Attribute | Description |
|---|---|
data-slot="table-row" | Target rows in CSS. |
data-state="selected" | Set it to highlight a selected row. |
| Attribute | Description |
|---|---|
data-slot="table-header" | The thead. |
data-slot="table-body" | The tbody. |
data-slot="table-footer" | The tfoot, on the header band color. |
data-slot="table-caption" | Below the table. In the surface variant it sits inside the frame. |