Skip to content
Areia
Esc
navigateopen⌘Jpreview
On this page

Table

A semantic table component for displaying tabular data with row variants, selection cells, and sticky columns.

Source Code

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 Email 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>
));

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.

Was this page helpful?