| Subject | From | Date |
|---|---|---|
| Kumo v1.0.0 released | Visal In | 5 seconds ago |
| New Job Offer | Cloudflare | 10 minutes ago |
| Daily Email Digest | Cloudflare | 1 hour ago |
<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>
<Table.Row>
<Table.Cell>Kumo v1.0.0 released</Table.Cell>
<Table.Cell>Visal In</Table.Cell>
<Table.Cell>5 seconds ago</Table.Cell>
</Table.Row>
</Table.Body>
</Table> Installation
Barrel
import { Table } from "@cloudflare/kumo"; Granular
import { Table } from "@cloudflare/kumo/components/table"; Usage
import { Table, LayerCard } from "@cloudflare/kumo";
export default function Example() {
return (
<LayerCard>
<LayerCard.Primary className="p-0">
<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>
</LayerCard.Primary>
</LayerCard>
);
} Examples
With Checkboxes
Add row selection with Table.CheckHead and Table.CheckCell .
| Subject | From | Date | |
|---|---|---|---|
| Kumo v1.0.0 released | Visal In | 5 seconds ago | |
| New Job Offer | Cloudflare | 10 minutes ago | |
| Daily Email Digest | Cloudflare | 1 hour ago |
<Table>
<Table.Header>
<Table.Row>
<Table.CheckHead />
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.CheckCell />
<Table.Cell>Email subject</Table.Cell>
<Table.Cell>Sender name</Table.Cell>
</Table.Row>
</Table.Body>
</Table> Compact Header
Use variant="compact" on Table.Header for a more condensed header style.
| Subject | From | Date |
|---|---|---|
| Kumo v1.0.0 released | Visal In | 5 seconds ago |
| New Job Offer | Cloudflare | 10 minutes ago |
| Daily Email Digest | Cloudflare | 1 hour ago |
<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>
<Table.Row>
<Table.Cell>Email subject</Table.Cell>
<Table.Cell>Sender name</Table.Cell>
<Table.Cell>Date</Table.Cell>
</Table.Row>
</Table.Body>
</Table> Selected Row
Use variant="selected" on Table.Row to highlight selected rows.
| Subject | From | Date | |
|---|---|---|---|
| Kumo v1.0.0 released | Visal In | 5 seconds ago | |
| New Job Offer | Cloudflare | 10 minutes ago | |
| Daily Email Digest | Cloudflare | 1 hour ago |
<Table.Row variant="selected">
<Table.CheckCell checked />
<Table.Cell>Selected row</Table.Cell>
</Table.Row> Fixed Layout with Column Sizes
For precise control over column widths, set layout=“fixed” and use colgroup with col elements.
| Subject | From | Date |
|---|---|---|
| Kumo v1.0.0 released | Visal In | 5 seconds ago |
| New Job Offer | Cloudflare | 10 minutes ago |
| Daily Email Digest | Cloudflare | 1 hour ago |
| GitLab - New Comment | Rob Knecht | 1 day ago |
| Out of Office | Johnnie Lappen | 3 days ago |
<Table layout="fixed">
<colgroup>
<col />
<col className="w-[150px]" />
<col className="w-[150px]" />
</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>
{data.map((row) => (
<Table.Row key={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> Full Example
Complete table with checkboxes, badges, action buttons, and fixed column widths.
| Subject | From | Date | ||
|---|---|---|---|---|
Kumo v1.0.0 released | Visal In | 5 seconds ago | ||
New Job Offer | Cloudflare | 10 minutes ago | ||
Daily Email Digest promotion | Cloudflare | 1 hour ago | ||
GitLab - New Comment | Rob Knecht | 1 day ago | ||
Out of Office | Johnnie Lappen | 3 days ago |
<Table layout="fixed">
<colgroup>
<col style={{ width: "40px" }} />
<col />
<col style={{ width: "150px" }} />
<col style={{ width: "120px" }} />
<col style={{ width: "50px" }} />
</colgroup>
<Table.Header>
<Table.Row>
<Table.CheckHead />
<Table.Head>Subject</Table.Head>
<Table.Head>From</Table.Head>
<Table.Head>Date</Table.Head>
<Table.Head></Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{data.map((row) => (
<Table.Row key={row.id} variant={row.selected ? "selected" : "default"}>
<Table.CheckCell checked={row.selected} />
<Table.Cell>
<div className="flex items-center gap-2">
<EnvelopeSimple size={16} />
{row.subject}
{row.tags?.map((tag) => <Badge key={tag}>{tag}</Badge>)}
</div>
</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
<Table.Cell className="text-right">
<Button variant="ghost" size="sm" shape="square">
<DotsThree weight="bold" size={16} />
</Button>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table> TanStack Table — Sortable & Resizable
Combine @tanstack/react-table with
Kumo’s Table for sortable columns
and draggable column resizing via Table.ResizeHandle.
Click any column header to sort; drag the resize handle on the right edge to change column width.
Worker | Requests | Errors | CPU (ms) | Status | |
|---|---|---|---|---|---|
| api-gateway | 142,830 | 12 | 4.2 ms | Active | |
| auth-service | 98,210 | 0 | 2.1 ms | Active | |
| image-resizer | 34,560 | 87 | 18.9 ms | Degraded | |
| cache-purger | 6,120 | 0 | 0.8 ms | Active | |
| log-drain | 0 | 0 | 0 ms | Inactive | |
| edge-router | 215,400 | 3 | 1.5 ms | Active |
import { useState } from "react";
import {
flexRender,
getCoreRowModel,
getSortedRowModel,
useReactTable,
type ColumnDef,
type SortingState,
} from "@tanstack/react-table";
import { Badge, LayerCard, Table } from "@cloudflare/kumo";
const columns: ColumnDef<Worker>[] = [
{ accessorKey: "name", header: "Worker", size: 200, minSize: 120 },
{
accessorKey: "requests",
header: "Requests",
size: 130,
minSize: 90,
cell: ({ getValue }) => (getValue() as number).toLocaleString(),
},
{ accessorKey: "errors", header: "Errors", size: 100, minSize: 70 },
{
accessorKey: "cpuMs",
header: "CPU (ms)",
size: 110,
minSize: 80,
cell: ({ getValue }) => `${getValue() as number} ms`,
},
{
accessorKey: "status",
header: "Status",
size: 110,
minSize: 80,
cell: ({ getValue }) => {
const status = getValue() as Worker["status"];
return <Badge variant={variantMap[status]}>{status}</Badge>;
},
},
];
export function DataTable() {
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
data: workerData,
columns,
state: { sorting },
onSortingChange: setSorting,
columnResizeMode: "onChange",
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
});
return (
<Table layout="fixed">
<colgroup>
{table.getAllColumns().map((col) => (
<col
key={col.id}
style={{ width: col.getSize() }}
className={
col.getIsResizing() ? "border-r border-kumo-ring" : undefined
}
/>
))}
{/* Filler column — absorbs remaining space so fixed columns don't stretch */}
<col />
</colgroup>
<Table.Header>
{table.getHeaderGroups().map((headerGroup) => (
<Table.Row key={headerGroup.id}>
{headerGroup.headers.map((header) => {
const isSorted = header.column.getIsSorted();
const canSort = header.column.getCanSort();
return (
<Table.Head
key={header.id}
aria-label={
canSort ? `Sort by ${header.column.id}` : undefined
}
className={
canSort ? "cursor-pointer select-none" : undefined
}
onClick={
canSort
? header.column.getToggleSortingHandler()
: undefined
}
>
<div className="flex items-center gap-1">
{header.isPlaceholder ? null : (
<>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
{canSort && <Table.SortIcon direction={isSorted} />}
</>
)}
</div>
<Table.ResizeHandle
onClick={(e) => {
e.stopPropagation();
}}
onMouseDown={(e) => {
header.getResizeHandler()(e);
}}
onTouchStart={(e) => {
header.getResizeHandler()(e);
}}
/>
</Table.Head>
);
})}
{/* Filler header cell matching the filler col */}
<Table.Head />
</Table.Row>
))}
</Table.Header>
<Table.Body>
{table.getRowModel().rows.map((row) => (
<Table.Row key={row.id}>
{row.getVisibleCells().map((cell) => (
<Table.Cell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Table.Cell>
))}
{/* Filler cell matching the filler col */}
<Table.Cell />
</Table.Row>
))}
</Table.Body>
</Table>
);
}
type Worker = {
id: string;
name: string;
requests: number;
errors: number;
cpuMs: number;
status: "Active" | "Inactive" | "Degraded";
};
const workerData: Worker[] = [
{ id: "1", name: "api-gateway", requests: 142_830, errors: 12, cpuMs: 4.2, status: "Active" },
{ id: "2", name: "auth-service", requests: 98_210, errors: 0, cpuMs: 2.1, status: "Active" },
{ id: "3", name: "image-resizer", requests: 34_560, errors: 87, cpuMs: 18.9, status: "Degraded" },
{ id: "4", name: "cache-purger", requests: 6_120, errors: 0, cpuMs: 0.8, status: "Active" },
{ id: "5", name: "log-drain", requests: 0, errors: 0, cpuMs: 0, status: "Inactive" },
{ id: "6", name: "edge-router", requests: 215_400, errors: 3, cpuMs: 1.5, status: "Active" },
];
const variantMap: Record<Worker["status"], "secondary" | "destructive" | "success"> = {
Active: "success",
Degraded: "destructive",
Inactive: "secondary",
}; API Reference
Table
Root table component. Renders a semantic <table> element.
| Prop | Type | Default | Description |
|---|---|---|---|
| layout | "auto" | "fixed" | "auto" | - |
| variant | "default" | "selected" | "default" | - |
| className | string | - | Additional CSS classes |
| children | ReactNode | - | Child elements |
Table.Header
Table header section. Renders <thead>.
Table.Body
Table body section. Renders <tbody>.
Table.Row
Table row. Supports variant="selected" for highlighting.
| Prop | Type | Default |
|---|
No component-specific props. Accepts standard HTML attributes.
Table.Head
Header cell. Renders <th>.
Table.Cell
Body cell. Renders <td>.
Table.CheckHead
Header cell with checkbox for “select all” functionality.
| Prop | Type | Default |
|---|
No component-specific props. Accepts standard HTML attributes.
Table.CheckCell
Body cell with checkbox for row selection.
| Prop | Type | Default |
|---|
No component-specific props. Accepts standard HTML attributes.
Table.ResizeHandle
Draggable handle for column resizing. Use with TanStack Table or custom resize logic.
Accessibility
Semantic HTML
Table uses semantic <table>, <thead>, <tbody>, <th>, and <td> elements for proper screen reader navigation.
Checkbox Labels
Always provide aria-label for Table.CheckHead and Table.CheckCell to describe their purpose.
Keyboard Navigation
Tab moves focus through interactive elements. Checkboxes respond to Space.