{
  "title": "DataTable",
  "description": "Convenience wrapper around Table. Pass columns + data arrays and get header rendering, in-memory sort, and client-side pagination for free.",
  "url": "/docs/components/data-table",
  "since": "0.7.0",
  "tags": [
    "data"
  ],
  "platform": "both",
  "source": "---\ntitle: DataTable\ndescription: Convenience wrapper around Table. Pass columns + data arrays and get header rendering, in-memory sort, and client-side pagination for free.\nsince: 0.7.0\ntags: [data]\nplatform: both\ncategory: data\n---\n\nimport { BundleSize } from '@/components/bundle-size';\nimport { Preview } from '@/components/preview';\n\n<BundleSize component=\"DataTable\" />\n\n## At a glance\n\n- Pass `columns` (header + cell renderer per column) and `data` (array of objects) — DataTable handles the rest.\n- In-memory, client-side sort on any column marked `sortable: true`. Click a header once for ascending, again for descending, a third time to clear.\n- Client-side pagination via the existing `usePagination` hook. Controls appear automatically when `data.length > pageSize`.\n- Empty state shown automatically when `data` is empty; override text or render any node via `emptyState`.\n- Row press handler via `onRowPress`.\n- Inherits all cosmetic props from `Table`: `striped`, `compact`, `bordered`.\n- For full control over markup and layout, use the lower-level [Table](/docs/components/table) primitive directly.\n\n## Basic usage\n\n<Preview name=\"data-table-basic\" />\n\n```tsx\nimport { DataTable } from '@nori-ui/core';\nimport type { Column } from '@nori-ui/core';\n\ntype Invoice = { id: string; customer: string; status: string; amount: number };\n\nconst columns: Column<Invoice>[] = [\n    { id: 'id',       header: 'Invoice',  cell: (r) => r.id },\n    { id: 'customer', header: 'Customer', cell: (r) => r.customer, sortable: true },\n    { id: 'status',   header: 'Status',   cell: (r) => r.status,   sortable: true },\n    { id: 'amount',   header: 'Amount',   cell: (r) => `$${r.amount.toFixed(2)}`, sortable: true, align: 'right' },\n];\n\nconst data: Invoice[] = [\n    { id: 'INV-001', customer: 'Alice', status: 'Paid',    amount: 250 },\n    { id: 'INV-002', customer: 'Bob',   status: 'Pending', amount: 150 },\n    // ...\n];\n\nexport function InvoiceDataTable() {\n    return <DataTable data={data} columns={columns} />;\n}\n```\n\n## With sort\n\nAny column with `sortable: true` gets a clickable header with a direction indicator (↑ / ↓). Clicking a third time clears the sort and restores the original data order.\n\nPass `defaultSort` to pre-sort on mount:\n\n```tsx\n<DataTable\n    data={data}\n    columns={columns}\n    defaultSort={{ id: 'amount', direction: 'asc' }}\n/>\n```\n\n## With pagination\n\nDataTable paginates automatically when `data.length > pageSize`. The default page size is 10. Pass a different value to `pageSize`:\n\n```tsx\n<DataTable data={largeDataset} columns={columns} pageSize={20} />\n```\n\nThe ‹ Prev / page / Next › controls appear below the table only when there is more than one page.\n\n## Empty state\n\nWhen `data` is empty DataTable shows \"No data\" centered below the header row. Override with any string or node:\n\n```tsx\n<DataTable data={[]} columns={columns} emptyState=\"No invoices found.\" />\n\n<DataTable\n    data={[]}\n    columns={columns}\n    emptyState={<MyEmptyIllustration message=\"Nothing here yet\" />}\n/>\n```\n\n## Custom cell rendering\n\n`cell` receives the full row and can return any `ReactNode`:\n\n```tsx\nconst columns: Column<User>[] = [\n    {\n        id: 'status',\n        header: 'Status',\n        cell: (row) => (\n            <Badge variant={row.status === 'active' ? 'success' : 'warning'}>\n                {row.status}\n            </Badge>\n        ),\n    },\n];\n```\n\n## Row press\n\n```tsx\n<DataTable\n    data={data}\n    columns={columns}\n    onRowPress={(row) => router.push(`/invoices/${row.id}`)}\n/>\n```\n\n## Accessibility\n\n- Column headers render as `<th>` elements via the underlying `Table.HeaderCell`. Screen readers associate data cells with their headers automatically.\n- Sortable headers include an `aria-label=\"Sort by <id>\"` on the interactive span.\n- Pagination controls use `aria-label` on Prev/Next and `aria-live=\"polite\"` on the page indicator.\n- The empty-state cell spans all columns via `colSpan` so it reads as a single region.\n\n## Deferred to v2\n\nThe following features are explicitly out of scope for v1 and are documented here so you know what to build around:\n\n| Feature | Notes |\n|---------|-------|\n| **Server-side sorting/pagination** | Add `onSort` / `onPaginate` callbacks; DataTable becomes uncontrolled for these. |\n| **Filtering** | Column-level filter inputs or a global search box. |\n| **Row selection** | Checkbox column + `selectedRows` / `onSelectionChange` props. |\n| **Expanding rows** | Sub-row renderer via `expandedRow` column. |\n| **Column resizing** | Drag handle on `HeaderCell`. |\n| **Sticky headers** | `stickyHeader` prop on `Table`; requires `overflow: auto` container. |\n| **Variable-width columns on native** | `width` / `minWidth` on `Column`; v1 uses equal flex. |\n\n## API\n\n### `Column<T>`\n\n```ts\ntype Column<T> = {\n    id: string;\n    header: ReactNode;\n    cell: (row: T) => ReactNode;\n    sortable?: boolean;\n    align?: 'left' | 'center' | 'right';\n};\n```\n\n### `DataTable`\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `data` | `T[]` | — | Row data array. |\n| `columns` | `Column<T>[]` | — | Column definitions. |\n| `pageSize` | `number` | `10` | Rows per page. |\n| `defaultSort` | `{ id: string; direction: 'asc' \\| 'desc' }` | — | Initial sort state. |\n| `onRowPress` | `(row: T) => void` | — | Row press/click handler. |\n| `emptyState` | `ReactNode` | `\"No data\"` | Shown when `data` is empty. |\n| `striped` | `boolean` | `false` | Inherited from `Table`. |\n| `compact` | `boolean` | `false` | Inherited from `Table`. |\n| `bordered` | `boolean` | `false` | Inherited from `Table`. |\n| `className` | `string` | — | Web only: extra classes on the table. |\n| `testID` | `string` | — | Test identifier. |\n"
}
