# Table

- Page: <https://kznlabs.github.io/labsUI/components/table>

## Table (`table`, component)

Server-safe semantic <table> in a scrolling card: header, body, footer and caption, striped rows, tinted selected rows, and sortable headers that set aria-sort and flip their arrow.

- Install: `npx @kznlabs/labsui add table`
- Docs: <https://kznlabs.github.io/labsUI/components/table>
- Brings: overflow-focus, utils
- npm: clsx@^2.1.1, lucide-react@1.63.0, tailwind-merge@^3.7.0

### API

- `<Table>` (+ <table> props): A real <table> in a card that scrolls sideways on narrow screens (and is keyboard-scrollable then).
  - `containerClassName?: string`: Classes for the scrolling card around the table.
  - `striped?: boolean` = `false`: Alternating row shading.
- `<TableHeader>` (+ <thead> props)
- `<TableBody>` (+ <tbody> props)
- `<TableFooter>` (+ <tfoot> props)
- `<TableRow>` (+ <tr> props): Pass `data-state="selected"` (or aria-selected) to tint a selected row.
- `<TableHead>` (+ <th> props)
  - `onSort?: () => void`: Called when the header button is pressed. Requires `sort`.
  - `sort?: "ascending" | "descending" | "none"`: Makes the column sortable: sets aria-sort and shows the direction arrow.
- `<TableCell>` (+ <td> props)
- `<TableCaption>` (+ <caption> props)

### Usage

```tsx
<Table striped><TableHeader><TableRow><TableHead sort="ascending" onSort={toggle}>Member</TableHead></TableRow></TableHeader><TableBody><TableRow><TableCell>Jane</TableCell></TableRow></TableBody></Table>
```

### Example: members-table.tsx

```tsx
"use client";

import { useState } from "react";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";

type Member = { name: string; messages: number };

export function MembersTable({ members }: { members: Member[] }) {
  const [dir, setDir] = useState<"ascending" | "descending">("descending");
  const rows = [...members].sort((a, b) =>
    dir === "ascending" ? a.messages - b.messages : b.messages - a.messages,
  );
  return (
    <Table striped>
      <TableHeader>
        <TableRow>
          <TableHead>Member</TableHead>
          <TableHead
            className="text-right"
            sort={dir}
            onSort={() => setDir(dir === "ascending" ? "descending" : "ascending")}
          >
            Messages
          </TableHead>
        </TableRow>
      </TableHeader>
      <TableBody>
        {rows.map((m) => (
          <TableRow key={m.name}>
            <TableCell className="font-medium">{m.name}</TableCell>
            <TableCell className="text-right tabular-nums">{m.messages}</TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}
```
