> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devreadykit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Table

> Flexible data table with compound components, built-in sorting, and row selection.

export const TableWithSelectionPreview = () => <iframe src="https://kerny-custom.vercel.app/preview/TableWithSelection" frameBorder="0" width="100%" height="400"></iframe>;

export const TableBasicPreview = () => <iframe src="https://kerny-custom.vercel.app/preview/TableBasic" frameBorder="0" width="100%" height="400"></iframe>;

## Overview

`Table` follows a compound-component API so you can compose headers, body rows, and cells declaratively while keeping visual styles and interactions consistent with the design system.

## Basic Usage

<Tabs>
  <Tab title="Code">
    ```tsx theme={null}
    import { Table } from '@peppermint-design/devreadykit-custom';

    type Invoice = {
      id: string;
      label: string;
      due: string;
      amount: number;
    };

    const invoices: Invoice[] = [
      { id: "inv-101", label: "Brand identity", due: "Jan 18, 2025", amount: 3800 },
      { id: "inv-102", label: "Website redesign", due: "Jan 22, 2025", amount: 5600 },
    ];

    export default function TableBasic() {
      return (
        <Table caption="Client invoices">
          <Table.Header>
            <Table.Head id="invoice" label="Invoice" isRowHeader />
            <Table.Head id="due" label="Due date" />
            <Table.Head id="amount" label="Amount" align="right" />
          </Table.Header>

          <Table.Body items={invoices}>
            {(invoice) => (
              <Table.Row id={invoice.id}>
                <Table.Cell>
                  <span className="font-semibold text-neutral-10">{invoice.label}</span>
                </Table.Cell>
                <Table.Cell>{invoice.due}</Table.Cell>
                <Table.Cell align="right">${invoice.amount.toLocaleString()}</Table.Cell>
              </Table.Row>
            )}
          </Table.Body>
        </Table>
      );
    }
    ```
  </Tab>

  <Tab title="Preview">
    <TableBasicPreview />
  </Tab>
</Tabs>

## Selection & Actions

Enable checkboxes by setting `selectionMode`. Rows register automatically and the header renders a "select all" checkbox for multiple selection.

<Tabs>
  <Tab title="Code">
    ```tsx theme={null}
    import { useState } from "react";
    import { Table, Badge } from "@peppermint-design/devreadykit-custom";
    import { Overflow } from "@/assets/icons/overflow";

    type Member = {
      id: string;
      name: string;
      role: string;
      status: "active" | "inactive";
    };

    const members: Member[] = [
      { id: "ava", name: "Ava Reynolds", role: "Designer", status: "active" },
      { id: "miles", name: "Miles Chen", role: "Engineering Manager", status: "inactive" },
    ];

    export default function TableWithSelection() {
      const [selected, setSelected] = useState<React.Key[]>([]);

      return (
        <Table
          selectionMode="multiple"
          selectedKeys={selected}
          onSelectionChange={setSelected}
          emptyState="No team members yet"
        >
          <Table.Header>
            <Table.Head id="name" label="Name" isRowHeader />
            <Table.Head id="status" label="Status" />
            <Table.Head id="role" label="Role" />
          </Table.Header>

          <Table.Body items={members}>
            {(member) => (
              <Table.Row id={member.id}>
                <Table.Cell>{member.name}</Table.Cell>
                <Table.Cell>
                  <Badge variant="status" color={member.status === "active" ? "available" : "offline"} />
                  <span className="ml-200 text-300 text-neutral-40 capitalize">{member.status}</span>
                </Table.Cell>
                <Table.Cell>{member.role}</Table.Cell>
              </Table.Row>
            )}
          </Table.Body>
        </Table>
      );
    }
    ```
  </Tab>

  <Tab title="Preview">
    <TableWithSelectionPreview />
  </Tab>
</Tabs>

## Sorting

Pass a controlled `sortDescriptor` to drive column sorting. Clicking any header with `allowsSorting` toggles between ascending and descending order.

<Tabs>
  <Tab title="Code">
    ```tsx theme={null}
    import { useMemo, useState } from "react";
    import { Table, type TableSortDescriptor } from "@peppermint-design/devreadykit-custom";

    type Customer = {
      id: string;
      name: string;
      total: number;
    };

    const customers: Customer[] = [
      { id: "1", name: "Andrew Martin", total: 760 },
      { id: "2", name: "Jessica Taylor", total: 540 },
    ];

    export default function SortableTable() {
      const [sortDescriptor, setSortDescriptor] = useState<TableSortDescriptor>({
        column: "name",
        direction: "ascending",
      });

      const sortedCustomers = useMemo(() => {
        const items = [...customers];
        const multiplier = sortDescriptor.direction === "ascending" ? 1 : -1;

        return items.sort((a, b) =>
          sortDescriptor.column === "total"
            ? (a.total - b.total) * multiplier
            : a.name.localeCompare(b.name) * multiplier,
        );
      }, [sortDescriptor]);

      return (
        <Table sortDescriptor={sortDescriptor} onSortChange={setSortDescriptor}>
          <Table.Header>
            <Table.Head id="name" label="Customer" allowsSorting isRowHeader />
            <Table.Head id="total" label="Lifetime value" allowsSorting align="right" />
          </Table.Header>

          <Table.Body items={sortedCustomers}>
            {(customer) => (
              <Table.Row id={customer.id}>
                <Table.Cell>{customer.name}</Table.Cell>
                <Table.Cell align="right">${customer.total.toLocaleString()}</Table.Cell>
              </Table.Row>
            )}
          </Table.Body>
        </Table>
      );
    }
    ```
  </Tab>

  <Tab title="Preview">
    <TableBasicPreview />
  </Tab>
</Tabs>

## Rich Cells

Compose cells with any JSX — avatars, badges, or multi-line content — while keeping spacing and typography consistent.

<Tabs>
  <Tab title="Code">
    ```tsx theme={null}
    import { Table, Avatar } from '@peppermint-design/devreadykit-custom';

    <Table.Cell>
      <div className="flex items-center gap-300">
        <Avatar size={40} name={member.name} />
        <div>
          <div className="text-350 font-semibold text-neutral-10">{member.name}</div>
          <div className="text-300 text-neutral-40">{member.email}</div>
        </div>
      </div>
    </Table.Cell>
    ```
  </Tab>

  <Tab title="Preview">
    <TableBasicPreview />
  </Tab>
</Tabs>

## Props

<PropsTable>
  | Prop                | Type                                        | Default               | Description                                                 |
  | :------------------ | :------------------------------------------ | :-------------------- | :---------------------------------------------------------- |
  | children            | `React.ReactNode`                           | –                     | Compose `Table.Header` and `Table.Body` inside the table.   |
  | caption             | `React.ReactNode`                           | –                     | Optional caption rendered before the header.                |
  | header              | `React.ReactNode`                           | –                     | Toolbar/header area rendered above the table content.       |
  | zebra               | `boolean`                                   | `false`               | Applies alternating background colors to body rows.         |
  | stickyHeader        | `boolean`                                   | `false`               | Makes the header stick to the top while scrolling.          |
  | rowClickable        | `boolean`                                   | `false`               | Adds pointer affordances and hover state to rows.           |
  | emptyState          | `React.ReactNode`                           | `"No data available"` | Content shown when the body has no rows.                    |
  | selectionMode       | `"none" \| "single" \| "multiple"`          | `"none"`              | Enables row selection. `"multiple"` adds a header checkbox. |
  | selectedKeys        | `React.Key[]`                               | –                     | Controlled selection keys.                                  |
  | defaultSelectedKeys | `React.Key[]`                               | `[]`                  | Initial selection for uncontrolled usage.                   |
  | onSelectionChange   | `(keys: React.Key[]) => void`               | –                     | Called when row selection changes.                          |
  | sortDescriptor      | `TableSortDescriptor \| null`               | `null`                | Controlled sorting state.                                   |
  | onSortChange        | `(descriptor: TableSortDescriptor) => void` | –                     | Called when a sortable header is clicked.                   |
  | tableClassName      | `string`                                    | –                     | Additional classes applied to the `<table>` element.        |
</PropsTable>

### `Table.Head`

| Prop          | Type                            | Default  | Description                                                                   |
| :------------ | :------------------------------ | :------- | :---------------------------------------------------------------------------- |
| id            | `string`                        | –        | Unique identifier for the column.                                             |
| label         | `React.ReactNode`               | –        | Header label. If omitted, children render instead.                            |
| allowsSorting | `boolean`                       | `false`  | Enables sort toggling on click.                                               |
| align         | `"left" \| "center" \| "right"` | `"left"` | Text alignment for the entire column.                                         |
| tooltip       | `string`                        | –        | Optional title attribute for the header.                                      |
| isRowHeader   | `boolean`                       | `false`  | Marks the column as a row header, rendering body cells as `<th scope="row">`. |

### `Table.Body`

| Prop     | Type                                                                           | Default | Description                              |
| :------- | :----------------------------------------------------------------------------- | :------ | :--------------------------------------- |
| items    | `T[]`                                                                          | –       | Data items to render.                    |
| children | `React.ReactNode \| (item: T, index: number) => React.ReactElement<Table.Row>` | –       | Either a render function or static rows. |

### `Table.Row`

| Prop       | Type        | Default | Description                                      |
| :--------- | :---------- | :------ | :----------------------------------------------- |
| id         | `React.Key` | –       | Row identifier used for selection.               |
| isDisabled | `boolean`   | `false` | Disables selection and hover styles for the row. |

### `Table.Cell`

| Prop        | Type                            | Default  | Description                                      |
| :---------- | :------------------------------ | :------- | :----------------------------------------------- |
| align       | `"left" \| "center" \| "right"` | `"left"` | Overrides alignment for the cell.                |
| isRowHeader | `boolean`                       | `false`  | Forces the cell to render as `<th scope="row">`. |

## Types

```ts theme={null}
type TableSelectionMode = "none" | "single" | "multiple";

type TableSortDirection = "ascending" | "descending";

type TableSortDescriptor = {
  column: string;
  direction: TableSortDirection;
};
```
