Table
A semantic table component for displaying tabular data with row variants, selection cells, and sticky columns.
Areia's Table renders platform table elements (table, thead, tbody, tr, th, td) with Areia styling and compound helpers for headers, cells, sticky columns, resize handles, and checkbox selection cells.
| Subject | From | Date |
|---|---|---|
| Welcome to Areia | team@example.com | Apr 12 |
| Build complete | ci@example.com | Apr 11 |
| New comment | noreply@example.com | Apr 10 |
import ilha from "ilha";
import { Table } from "areia";
const rows = [
{
subject: "Welcome to Areia",
from: "team@example.com",
date: "Apr 12",
},
{
subject: "Build complete",
from: "ci@example.com",
date: "Apr 11",
},
{
subject: "New comment",
from: "noreply@example.com",
date: "Apr 10",
},
];
export default ilha.render(() => (
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
));Import
import { Table } from "areia";
Usage
| Name | Role | |
|---|---|---|
| John Doe | john@example.com | Admin |
import ilha from "ilha";
import { Table } from "areia";
export default ilha.render(() => (
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Name</Table.Head>
<Table.Head>Email</Table.Head>
<Table.Head>Role</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>Admin</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
));Examples
With Checkboxes
Add selection columns with Table.CheckHead and Table.CheckCell. They render Areia checkboxes with larger cell hit targets.
import ilha from "ilha";
import { Table } from "areia";
const rows = [
{
id: "1",
subject: "Welcome to Areia",
from: "team@example.com",
date: "Apr 12",
},
{
id: "2",
subject: "Build complete",
from: "ci@example.com",
date: "Apr 11",
},
{
id: "3",
subject: "New comment",
from: "noreply@example.com",
date: "Apr 10",
},
];
const selectedIds = new Set(["2"]);
export default ilha.render(() => (
<Table>
<Table.Header>
<Table.Row>
<Table.CheckHead
checked={selectedIds.size === rows.length}
indeterminate={
selectedIds.size > 0 &&
selectedIds.size < rows.length
}
label="Select all rows"
/>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row
variant={
selectedIds.has(row.id) ? "selected" : "default"
}
>
<Table.CheckCell
checked={selectedIds.has(row.id)}
label={`Select ${row.subject}`}
value={row.id}
/>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
));Compact Header
Use variant="compact" on Table.Header for a more condensed header style.
| Subject | From | Date |
|---|---|---|
| Welcome to Areia | team@example.com | Apr 12 |
| Build complete | ci@example.com | Apr 11 |
| New comment | noreply@example.com | Apr 10 |
import ilha from "ilha";
import { Table } from "areia";
const rows = [
{
subject: "Welcome to Areia",
from: "team@example.com",
date: "Apr 12",
},
{
subject: "Build complete",
from: "ci@example.com",
date: "Apr 11",
},
{
subject: "New comment",
from: "noreply@example.com",
date: "Apr 10",
},
];
export default ilha.render(() => (
<Table>
<Table.Header variant="compact">
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
));Selected Row
Use variant="selected" on Table.Row to highlight selected rows.
import ilha from "ilha";
import { Table } from "areia";
const rows = [
{
id: "1",
subject: "Welcome to Areia",
from: "team@example.com",
date: "Apr 12",
},
{
id: "2",
subject: "Build complete",
from: "ci@example.com",
date: "Apr 11",
},
{
id: "3",
subject: "New comment",
from: "noreply@example.com",
date: "Apr 10",
},
];
export default ilha.render(() => (
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row
variant={row.id === "2" ? "selected" : "default"}
>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
));Fixed Layout with Column Sizes
For precise control over column widths, set layout="fixed" and provide a colgroup.
| Subject | From | Date |
|---|---|---|
| Welcome to Areia | team@example.com | Apr 12 |
| Build complete | ci@example.com | Apr 11 |
| New comment | noreply@example.com | Apr 10 |
import ilha from "ilha";
import { Table } from "areia";
const rows = [
{
subject: "Welcome to Areia",
from: "team@example.com",
date: "Apr 12",
},
{
subject: "Build complete",
from: "ci@example.com",
date: "Apr 11",
},
{
subject: "New comment",
from: "noreply@example.com",
date: "Apr 10",
},
];
export default ilha.render(() => (
<Table layout="fixed">
<colgroup>
<col class="w-1/2" />
<col class="w-1/4" />
<col class="w-1/4" />
</colgroup>
<Table.Header>
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
));Sticky Column
Pin a column to the left or right edge of a horizontal scroll container with sticky="left" or sticky="right" on Table.Head and Table.Cell.
import ilha from "ilha";
import { Badge, Table } from "areia";
const rows = [
{
subject: "Welcome to Areia",
from: "team@example.com",
date: "Apr 12",
tags: ["docs"],
},
{
subject: "Build complete",
from: "ci@example.com",
date: "Apr 11",
tags: ["ci", "deploy"],
},
{
subject: "New comment",
from: "noreply@example.com",
date: "Apr 10",
tags: ["inbox"],
},
];
export default ilha.render(() => (
<div class="w-full overflow-x-auto">
<Table class="min-w-[720px]">
<Table.Header>
<Table.Row>
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
<Table.Head>Tags</Table.Head>
<Table.Head sticky="right">Actions</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
<Table.Cell>
<div class="flex gap-1">
{row.tags.map((tag) => (
<Badge variant="neutral">{tag}</Badge>
))}
</div>
</Table.Cell>
<Table.Cell sticky="right">
<button class="text-sm underline">View</button>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</div>
));Sticky Header
Set sticky on Table.Header and put the table in a height-constrained vertical scroll container.
import ilha from "ilha";
import { Table } from "areia";
const rows = Array.from({ length: 12 }, (_, index) => ({
name: `Component ${index + 1}`,
status: index % 2 === 0 ? "Stable" : "Draft",
}));
export default ilha.render(() => (
<div class="max-h-64 overflow-y-auto">
<Table>
<Table.Header sticky>
<Table.Row>
<Table.Head>Name</Table.Head>
<Table.Head>Status</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row>
<Table.Cell>{row.name}</Table.Cell>
<Table.Cell>{row.status}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</div>
));Resize Handle
Table.ResizeHandle resizes its containing th or td by updating the cell width while you drag it. Place it inside a relatively positioned cell.
|
Name | Role |
|---|
import ilha from "ilha";
import { Table } from "areia";
export default ilha.render(() => (
<Table>
<Table.Header>
<Table.Row>
<Table.Head class="relative">
Name
<Table.ResizeHandle />
</Table.Head>
<Table.Head>Role</Table.Head>
</Table.Row>
</Table.Header>
</Table>
));API Reference
Table
Root table component. Renders a semantic <table> element.
| Prop | Type | Default | Description |
|---|---|---|---|
layout | "auto" | "fixed" | "auto" | Table layout algorithm. |
children | unknown | - | Table sections, rows, and optional colgroup. |
class | string | - | Additional CSS classes applied to the table. |
className | string | - | Alias for class. |
Table.Header
Table header section. Renders <thead>.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "compact" | "default" | Header style. |
sticky | boolean | - | Make header cells stick to the top of the scroll container. |
children | unknown | - | Header rows. |
class | string | - | Additional CSS classes. |
className | string | - | Alias for class. |
Table.Row
Table row. Supports variant: "selected" for highlighting.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "selected" | "default" | Row visual variant. |
children | unknown | - | Row cells. |
class | string | - | Additional CSS classes. |
className | string | - | Alias for class. |
Table.Head
Header cell. Renders <th>.
| Prop | Type | Default | Description |
|---|---|---|---|
sticky | "left" | "right" | - | Pin the header cell horizontally. |
children | unknown | - | Cell content. |
class | string | - | Additional CSS classes. |
className | string | - | Alias for class. |
Table.Cell
Body cell. Renders <td>.
| Prop | Type | Default | Description |
|---|---|---|---|
sticky | "left" | "right" | - | Pin the body cell horizontally. |
children | unknown | - | Cell content. |
class | string | - | Additional CSS classes. |
className | string | - | Alias for class. |
Table.Body and Table.Footer
Table.Body renders <tbody>. Table.Footer renders <tfoot>. Both accept standard table section attributes plus children, class, and className.
Table.CheckHead and Table.CheckCell
Selection cells with Areia checkboxes.
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | - | Whether the checkbox is checked. |
indeterminate | boolean | - | Whether the checkbox is visually mixed. |
label | string | - | Accessible label. |
disabled | boolean | - | Whether the checkbox is disabled. |
name | string | - | Native checkbox name. |
value | string | - | Native checkbox value. |
Table.ResizeHandle
Handle for column resizing. Dragging it updates the containing th or td width inline.
| Prop | Type | Default | Description |
|---|---|---|---|
minWidth | number | 40 | Minimum cell width in pixels while dragging. |
class | string | - | Additional CSS classes. |
className | string | - | Alias for class. |
TanStack Table Integration
For advanced features like sorting, filtering, and resizable columns, integrate with TanStack Table. Areia's Table is semantic and unopinionated, so you can render TanStack header groups, rows, and cells through the same compound helpers.
import { Table } from "areia";
// Pseudo-code: use your TanStack table instance to render headers and cells.
const markup = Table({
children: [
Table.Header({ children: "...header groups" }),
Table.Body({ children: "...row model" }),
],
});
Accessibility
Semantic HTML
Table uses semantic <table>, <thead>, <tbody>, <tfoot>, <tr>, <th>, and <td> elements for screen reader navigation.
Checkbox Labels
Always provide label for Table.CheckHead and Table.CheckCell so the generated checkboxes have useful accessible names.
Keyboard Navigation
Tab moves focus through interactive elements. Checkboxes respond to Space using native checkbox behavior.