Table
Shows records in rows and columns so people can compare them, like invoices or stock. For sorting, filtering and pages of data, use Data table.
Installation
Set up Camaleo once (Installation), then import what you need. It renders on the server.
import { Table, TableCaption, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell } from "@camaleo/react";Anatomy
A native <table> inside a container that scrolls sideways when the table is too wide. Every part has a data-slot you can target from CSS.
Examples
Every example is a real file you can copy. They all wear the skin, mode and brand from the bar above.
| Invoice | Status | Amount |
|---|---|---|
| INV-4029 | Paid | $340.00 |
| INV-4028 | Pending | $1,280.50 |
| INV-4027 | Overdue | $75.00 |
| Invoice | Issued | Amount |
|---|---|---|
| INV-4029 | 10/14/2026 | $340.00 |
| INV-4028 | 10/12/2026 | $1,280.50 |
With a caption
A TableCaption names the table. Screen readers read it before the rows, and it also names the scroll area when the table gets too wide.
| Customer | Orders | Revenue |
|---|---|---|
| Acme Logistics | 142 | $48,200.00 |
| Vila Nova Design | 89 | $24,650.00 |
| Aurora BioTech | 12 | $8,400.00 |
Numbers on the right
Pass align="end" to the header and the cells of number columns. Digits get the same width, so people can compare values at a glance.
| Invoice | Customer | Amount |
|---|---|---|
| INV-4029 | Acme Logistics | $340.00 |
| INV-4028 | Vila Nova Design | $1,280.50 |
| INV-4027 | Aurora BioTech | $75.00 |
| Total | $1,695.50 | |
Totals row
Put the total in a TableFooter, below a stronger line. It stays apart from the data rows, so sorting or adding rows never moves it.
| Product | Stock | Price |
|---|---|---|
| Mechanical keyboard TKL | In stock | $149.00 |
| Wireless ergonomic mouse | 3 left | $89.00 |
| USB-C hub, 8 ports | Out of stock | $65.00 |
Status in a column
A badge per row is easier to scan than a sentence. Keep the words short and use the same ones in every row.
| Invoice | Customer | Amount |
|---|---|---|
No invoices yet Invoices you send to customers show up here. | ||
No rows yet
Keep the headers and fill the body with one row that spans every column. Say why it is empty and give the next step.
| Customer | |
|---|---|
| Vila Nova Design | $1,250.00 |
| Acme Logistics | $4,800.00 |
| Aurora BioTech | $9,320.00 |
Sortable column
Click Revenue. The button flips the order, and aria-sort on the header tells screen readers which way the rows are sorted.
| Invoice | Customer | Issued | Due | Payment | Status | Amount |
|---|---|---|---|---|---|---|
| INV-4029 | Acme Logistics | 10/14/2026 | 11/13/2026 | Card | Paid | $340.00 |
| INV-4028 | Vila Nova Design | 10/12/2026 | 11/11/2026 | Bank transfer | Pending | $1,280.50 |
| INV-4027 | Aurora BioTech | 10/01/2026 | 10/15/2026 | Card | Overdue | $75.00 |
Wide table on a phone
On a narrow screen this table scrolls sideways. Only then the container becomes a named scroll area with one Tab stop, so keyboard users can scroll it with the arrow keys.
In context
Tables usually sit in a card or a page section, with actions in the last column. These are whole pieces of UI you can copy, in the skin you picked.
| Invoice | Issued | Status | Amount | |
|---|---|---|---|---|
| INV-4029 | 10/14/2026 | Paid | $340.00 | |
| INV-4028 | 10/12/2026 | Pending | $1,280.50 | |
| INV-4027 | 10/01/2026 | Overdue | $75.00 |
| Name | Role | Status | |
|---|---|---|---|
APAline Prado | Frontend architect | aline@camaleo.dev | Active |
TCTiago Costa | DevOps engineer | tiago@camaleo.dev | Away |
| SKU | Item | Quantity | Unit price |
|---|---|---|---|
| SKU-8921 | Aluminum monitor arm | 28 | $129.00 |
| SKU-8922 | Wool felt desk mat, large | 114 | $34.50 |
| SKU-8923 | Oak laptop stand | 0 | $58.00 |
Guidelines
| Customer | Amount |
|---|---|
| Acme Logistics | $24,650.00 |
| Vila Nova Design | $75.00 |
Units line up with units, so the biggest value stands out without reading every row.
| Customer | Amount |
|---|---|
| Acme Logistics | $24,650.00 |
| Vila Nova Design | $75.00 |
Decimals end up in different places, and people have to read each value to compare.
| Invoice | Amount |
|---|---|
| INV-4029 | $340.00 |
Use a TableCaption, or aria-label when the title is already on the page. People hear what the data is before the first row.
| Column A | Column B |
|---|---|
| Data 1 | 120.00 |
Screen readers read the header with each cell. A vague header makes every cell vague.
Props
<caption>.end for numbers: it aligns them to the right and gives every
digit the same width, so values line up down the column.TableHead and TableCell take align. Every part also takes the attributes of its HTML element, like colSpan and aria-sort. TableCaption (a <caption>, first inside Table) and TableFooter (a <tfoot>) take the attributes of their elements.
Styling hooks
Restyle it without forking: target the attributes, or set the tokens on a parent, a skin or the table itself.
Attributes
Tokens it reads
Color
Shape & size
Type & motion
This component only
Accessibility
- TableHead adds scope="col", so screen readers read the column header with each cell.
- A table that doesn't fit becomes a scroll area (role="region") named by the caption or scrollLabel. A table that fits adds no Tab stop.
- Name every table with a TableCaption, or with aria-label when a heading already says what it is.
- Icon buttons in a row need a name that includes the row, like "Download INV-4029", not just "Download".