Table
Display tabular data in a grid with selection and sorting.
- Documentation
- React Aria
- Pattern
- W3C ARIA
- Source
- GitHub
- Issues
- Report
- Composes
- Checkbox
- Install
pnpm dlx @kkga/shim add table
Name | Type |
|---|---|
| Games | File folder |
| Program Files | File folder |
| bootmgr | System file |
| Documents | File folder |
| Downloads | File folder |
import {
Cell,
Column,
Row,
Table,
TableBody,
TableHeader,
} from "@/shim-ui/table";
export default () => (
<Table aria-label="Files" className="table-fixed">
<TableHeader>
<Column isRowHeader>Name</Column>
<Column>Type</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>Games</Cell>
<Cell>File folder</Cell>
</Row>
<Row>
<Cell>Program Files</Cell>
<Cell>File folder</Cell>
</Row>
<Row>
<Cell>bootmgr</Cell>
<Cell>System file</Cell>
</Row>
<Row>
<Cell>Documents</Cell>
<Cell>File folder</Cell>
</Row>
<Row>
<Cell>Downloads</Cell>
<Cell>File folder</Cell>
</Row>
</TableBody>
</Table>
);Install
Use the CLI or copy the source code manually.
pnpm dlx @kkga/shim add tableContent
Provide columns via the columns prop on <TableHeader> and data with items on <TableBody> to render dynamic tables.
Creature | Can fly | Has horn | Scary |
|---|---|---|---|
| Unicorn | |||
| Dragon | |||
| Mermaid |
"use client";
import { CheckIcon, XIcon } from "@phosphor-icons/react";
import {
Cell,
Column,
Row,
Table,
TableBody,
TableHeader,
} from "@/shim-ui/table";
let columns = [
{ id: "creature", name: "Creature", isRowHeader: true },
{ id: "canFly", name: "Can fly" },
{ id: "hasHorn", name: "Has horn" },
{ id: "scary", name: "Scary" },
];
let rows = [
{
id: 1,
creature: "Unicorn",
canFly: false,
hasHorn: true,
scary: false,
},
{
id: 2,
creature: "Dragon",
canFly: true,
hasHorn: false,
scary: true,
},
{
id: 3,
creature: "Mermaid",
canFly: false,
hasHorn: false,
scary: false,
},
];
const Check = () => (
<CheckIcon className="text-success-text" size={16} weight="bold" />
);
const X = () => <XIcon className="text-neutral-text-subtle" size={16} />;
export default () => (
<Table aria-label="Mythical Creatures" className="table-fixed">
<TableHeader columns={columns}>
{(column) => (
<Column isRowHeader={column.isRowHeader}>{column.name}</Column>
)}
</TableHeader>
<TableBody items={rows}>
{({ creature, canFly, hasHorn, scary }) => (
<Row>
<Cell>{creature}</Cell>
<Cell className="align-middle">{canFly ? <Check /> : <X />}</Cell>
<Cell className="align-middle">{hasHorn ? <Check /> : <X />}</Cell>
<Cell className="align-middle">{scary ? <Check /> : <X />}</Cell>
</Row>
)}
</TableBody>
</Table>
);Sorting
Enable column sorting by providing a sortDescriptor with a column key and direction value. Direction accepts "ascending" or "descending" and the column matches the column key.
Name | Major | GPA |
|---|---|---|
| Alice Johnson | Mathematics | 3.9 |
| Bob Brown | Physics | 3.7 |
| Jane Smith | Mechanical Engineering | 3.8 |
| John Doe | Computer Science | 3.5 |
"use client";
import { useMemo, useState } from "react";
import type { SortDescriptor } from "react-aria-components";
import {
Cell,
Column,
Row,
Table,
TableBody,
TableHeader,
} from "@/shim-ui/table";
interface RowData {
id: number;
name: string;
major: string;
gpa: number;
}
let rows: RowData[] = [
{ id: 1, name: "John Doe", major: "Computer Science", gpa: 3.5 },
{ id: 2, name: "Jane Smith", major: "Mechanical Engineering", gpa: 3.8 },
{ id: 3, name: "Alice Johnson", major: "Mathematics", gpa: 3.9 },
{ id: 4, name: "Bob Brown", major: "Physics", gpa: 3.7 },
];
export default () => {
let [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
column: "name",
direction: "ascending",
});
let items = useMemo(() => {
let sorted = [...rows].sort((a, b) => {
let column = sortDescriptor.column as keyof RowData;
if (typeof a[column] === "number" && typeof b[column] === "number") {
return a[column] - b[column];
}
if (typeof a[column] === "string" && typeof b[column] === "string") {
return a[column].localeCompare(b[column]);
}
return 0;
});
if (sortDescriptor.direction === "descending") {
sorted.reverse();
}
return sorted;
}, [sortDescriptor]);
return (
<Table
aria-label="Students"
onSortChange={setSortDescriptor}
sortDescriptor={sortDescriptor}
>
<TableHeader>
<Column allowsSorting id="name" isRowHeader>
Name
</Column>
<Column allowsSorting id="major">
Major
</Column>
<Column allowsSorting id="gpa">
GPA
</Column>
</TableHeader>
<TableBody items={items}>
{({ name, major, gpa }) => (
<Row>
<Cell>{name}</Cell>
<Cell>{major}</Cell>
<Cell className="tabular-nums">{gpa}</Cell>
</Row>
)}
</TableBody>
</Table>
);
};Selection
Tables support single and multiple row selection via the selectionMode prop. Set it to "single" or "multiple" to enable selection. Use selectedKeys and onSelectionChange to control the selected rows.
Name | Type | Date Modified | |
|---|---|---|---|
| Games | File folder | 6/7/2020 | |
| Program Files | File folder | 4/7/2021 | |
| bootmgr | System file | 11/20/2010 |
"use client";
import {
Cell,
Column,
Row,
Table,
TableBody,
TableHeader,
} from "@/shim-ui/table";
export default () => (
<Table aria-label="Files" selectionMode="multiple">
<TableHeader>
<Column isRowHeader>Name</Column>
<Column>Type</Column>
<Column>Date Modified</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>Games</Cell>
<Cell>File folder</Cell>
<Cell>6/7/2020</Cell>
</Row>
<Row>
<Cell>Program Files</Cell>
<Cell>File folder</Cell>
<Cell>4/7/2021</Cell>
</Row>
<Row>
<Cell>bootmgr</Cell>
<Cell>System file</Cell>
<Cell>11/20/2010</Cell>
</Row>
</TableBody>
</Table>
);Variant
Tables support three visual variants: "surface", "ghost", and "zebra". The "surface" variant has a solid background and borders, while the "ghost" variant has a transparent background and no borders. The "zebra" variant alternates row background colors for better readability.
Name | Role |
|---|---|
| John Doe | Administrator |
| Jane Smith | Editor |
| Sam Johnson | Subscriber |
Name | Role |
|---|---|
| John Doe | Administrator |
| Jane Smith | Editor |
| Sam Johnson | Subscriber |
Name | Role |
|---|---|
| John Doe | Administrator |
| Jane Smith | Editor |
| Sam Johnson | Subscriber |
import {
Cell,
Column,
Row,
Table,
TableBody,
TableHeader,
} from "@/shim-ui/table";
export default () =>
(["zebra", "ghost", "surface"] as const).map((variant) => (
<Table
aria-label="People"
className="table-fixed"
key={variant}
variant={variant}
>
<TableHeader>
<Column className="w-1/3" isRowHeader>
Name
</Column>
<Column>Role</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>John Doe</Cell>
<Cell>Administrator</Cell>
</Row>
<Row>
<Cell>Jane Smith</Cell>
<Cell>Editor</Cell>
</Row>
<Row>
<Cell>Sam Johnson</Cell>
<Cell>Subscriber</Cell>
</Row>
</TableBody>
</Table>
));Size
Tables support four sizes that adjust the padding and typography of the table elements. The sizes are numbered from 1 to 4, with 1 being the smallest and 4 the largest.
Name | Role | |
|---|---|---|
| John Doe | Administrator | |
| Jane Smith | Editor |
Name | Role | |
|---|---|---|
| John Doe | Administrator | |
| Jane Smith | Editor |
Name | Role | |
|---|---|---|
| John Doe | Administrator | |
| Jane Smith | Editor |
Name | Role | |
|---|---|---|
| John Doe | Administrator | |
| Jane Smith | Editor |
import {
Cell,
Column,
Row,
Table,
TableBody,
TableHeader,
} from "@/shim-ui/table";
export default () =>
([1, 2, 3, 4] as const).map((size) => (
<Table
aria-label="People"
className="table-fixed"
key={size}
selectionMode="multiple"
size={size}
>
<TableHeader>
<Column className="w-1/3" isRowHeader>
Name
</Column>
<Column>Role</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>John Doe</Cell>
<Cell>Administrator</Cell>
</Row>
<Row>
<Cell>Jane Smith</Cell>
<Cell>Editor</Cell>
</Row>
</TableBody>
</Table>
));