---
layout: ~/layouts/MdxDocLayout.astro
title: "Table"
description: "A table component for displaying tabular data with support for selection, row variants, and column sizing."
sourceFile: "components/table"
---

import ComponentExample from "~/components/docs/ComponentExample.astro";
import ComponentSection from "~/components/docs/ComponentSection.astro";
import Heading from "~/components/docs/Heading.astro";
import PropsTable from "~/components/docs/PropsTable.astro";
import {
  TableBasicDemo,
  TableWithCheckboxDemo,
  TableWithCompactHeaderDemo,
  TableSelectedRowDemo,
  TableFixedLayoutDemo,
  TableStickyColumnDemo,
  TableFullDemo,
} from "~/components/demos/TableDemo";

{/* Hero Demo */}

<ComponentSection>
  <ComponentExample demo="TableBasicDemo">
    <TableBasicDemo client:visible />
  </ComponentExample>
</ComponentSection>

{/* Installation */}

<ComponentSection>
  <Heading level={2}>Installation</Heading>
  <Heading level={3}>Barrel</Heading>

```tsx
import { Table } from "@cloudflare/kumo";
```

<Heading level={3}>Granular</Heading>

```tsx
import { Table } from "@cloudflare/kumo/components/table";
```

</ComponentSection>

{/* Usage */}

<ComponentSection>
  <Heading level={2}>Usage</Heading>

```tsx
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>
  );
}
```

</ComponentSection>

{/* Examples */}

<ComponentSection>
  <Heading level={2}>Examples</Heading>

<Heading level={3}>With Checkboxes</Heading>
<p>Add row selection with `Table.CheckHead` and `Table.CheckCell`.</p>
<ComponentExample demo="TableWithCheckboxDemo">
  <TableWithCheckboxDemo client:visible />
</ComponentExample>

<Heading level={3}>Compact Header</Heading>
<p>
  Use `variant="compact"` on `Table.Header` for a more condensed header style.
</p>
<ComponentExample demo="TableWithCompactHeaderDemo">
  <TableWithCompactHeaderDemo client:visible />
</ComponentExample>

<Heading level={3}>Selected Row</Heading>
<p>Use `variant="selected"` on `Table.Row` to highlight selected rows.</p>
<ComponentExample demo="TableSelectedRowDemo">
  <TableSelectedRowDemo client:visible />
</ComponentExample>

<Heading level={3}>Fixed Layout with Column Sizes</Heading>
<p>
  For precise control over column widths, set `layout="fixed"` and use
  `colgroup` with `col` elements.
</p>
<ComponentExample demo="TableFixedLayoutDemo">
  <TableFixedLayoutDemo client:visible />
</ComponentExample>

<Heading level={3}>Sticky Column</Heading>
<p>
  Pin a column to the left or right edge of the scroll container with
  `sticky="left"` or `sticky="right"` on `Table.Head` and `Table.Cell`. The
  component automatically adds an opaque background and gradient fade. Wrap the
  table in an `overflow-x-auto` container.
</p>
<ComponentExample demo="TableStickyColumnDemo">
  <TableStickyColumnDemo client:visible />
</ComponentExample>

<Heading level={3}>Full Example</Heading>
<p>
  Complete table with checkboxes, badges, action buttons, and fixed column
  widths.
</p>
<ComponentExample demo="TableFullDemo">
  <TableFullDemo client:visible />
</ComponentExample>

</ComponentSection>

{/* API Reference */}

<ComponentSection>
  <Heading level={2}>API Reference</Heading>

<Heading level={3}>Table</Heading>
<p>Root table component. Renders a semantic `<table>` element.</p>
<PropsTable component="Table" />

<Heading level={3}>Table.Header</Heading>
<p>Table header section. Renders `<thead>`. Set `sticky` to pin the header row to the top of the scroll container.</p>

<Heading level={3}>Table.Body</Heading>
<p>Table body section. Renders `<tbody>`.</p>

<Heading level={3}>Table.Row</Heading>
<p>Table row. Supports `variant="selected"` for highlighting.</p>
<PropsTable component="Table.Row" />

<Heading level={3}>Table.Head</Heading>
<p>Header cell. Renders `<th>`. Accepts `sticky="left"` or `sticky="right"` to pin the column.</p>

<Heading level={3}>Table.Cell</Heading>
<p>Body cell. Renders `<td>`. Accepts `sticky="left"` or `sticky="right"` to pin the column.</p>

<Heading level={3}>Table.CheckHead</Heading>
<p>Header cell with checkbox for "select all" functionality.</p>
<PropsTable component="Table.CheckHead" />

<Heading level={3}>Table.CheckCell</Heading>
<p>Body cell with checkbox for row selection.</p>
<PropsTable component="Table.CheckCell" />

<Heading level={3}>Table.ResizeHandle</Heading>
<p>
  Draggable handle for column resizing. Use with TanStack Table or custom resize
  logic.
</p>

</ComponentSection>

{/* TanStack Table Integration */}

<ComponentSection>
  <Heading level={2}>TanStack Table Integration</Heading>
  <p>
    For advanced features like sorting, filtering, and resizable columns, integrate with <a href="https://tanstack.com/table" class="text-kumo-link hover:underline" target="_blank" rel="noopener noreferrer">TanStack Table</a>. The Table component is designed to work seamlessly with TanStack's headless API.
  </p>

```tsx
import {
  flexRender,
  getCoreRowModel,
  useReactTable,
} from "@tanstack/react-table";
import { Table } from "@cloudflare/kumo";

function DataTable({ data, columns }) {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    columnResizeMode: "onChange",
  });

  return (
    <Table layout="fixed">
      <colgroup>
        {table.getAllColumns().map((column) => (
          <col key={column.id} style={{ width: column.getSize() }} />
        ))}
      </colgroup>
      <Table.Header>
        {table.getHeaderGroups().map((headerGroup) => (
          <Table.Row key={headerGroup.id}>
            {headerGroup.headers.map((header) => (
              <Table.Head key={header.id}>
                {flexRender(
                  header.column.columnDef.header,
                  header.getContext(),
                )}
                <Table.ResizeHandle
                  onMouseDown={header.getResizeHandler()}
                  onTouchStart={header.getResizeHandler()}
                />
              </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>
            ))}
          </Table.Row>
        ))}
      </Table.Body>
    </Table>
  );
}
```

</ComponentSection>

{/* Accessibility */}

<ComponentSection>
  <Heading level={2}>Accessibility</Heading>

<h3 class="mb-2 font-semibold">Semantic HTML</h3>
<p class="text-kumo-strong">
  Table uses semantic `<table>`, `<thead>`, `<tbody>`, `<th>`, and `<td>` elements for proper screen reader navigation.
</p>

<h3 class="mb-2 mt-4 font-semibold">Checkbox Labels</h3>
<p class="text-kumo-strong">
  Always provide `aria-label` for `Table.CheckHead` and `Table.CheckCell` to
  describe their purpose.
</p>

<h3 class="mb-2 mt-4 font-semibold">Keyboard Navigation</h3>
<p class="text-kumo-strong">
  <kbd class="rounded bg-kumo-control px-1.5 py-0.5">Tab</kbd> moves focus
  through interactive elements. Checkboxes respond to{" "}
  <kbd class="rounded bg-kumo-control px-1.5 py-0.5">Space</kbd>.
</p>

</ComponentSection>
