{
  "title": "Table",
  "description": "Compound table primitive. Semantic <table> on web for full a11y; View flex-grid on native.",
  "url": "/docs/components/table",
  "since": "0.7.0",
  "tags": [
    "data"
  ],
  "platform": "both",
  "source": "---\ntitle: Table\ndescription: Compound table primitive. Semantic <table> on web for full a11y; View flex-grid on native.\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=\"Table\" />\n\n## At a glance\n\n- Compound primitive: `Table`, `.Header`, `.Body`, `.Footer`, `.Row`, `.HeaderCell`, `.Cell`, `.Caption`.\n- Web uses semantic `<table>` / `<thead>` / `<tbody>` / `<tfoot>` / `<tr>` / `<th>` / `<td>` / `<caption>` for full screen-reader support.\n- Native renders a `ScrollView` + `View` flex-grid (all columns equal-width in v1; variable widths are a v2 improvement).\n- Optional cosmetic flags: `striped`, `compact`, `bordered`.\n- Interactive rows via `onPress` on `Table.Row`.\n- For automatic column/data rendering with sort + pagination, see [DataTable](/docs/components/data-table).\n\n## Basic usage\n\n<Preview name=\"table-basic\" />\n\n```tsx\nimport { Table } from '@nori-ui/core';\n\nconst invoices = [\n    { id: 'INV-001', status: 'Paid', amount: '$250.00' },\n    { id: 'INV-002', status: 'Pending', amount: '$150.00' },\n    { id: 'INV-003', status: 'Overdue', amount: '$350.00' },\n];\n\nexport function InvoiceTable() {\n    return (\n        <Table>\n            <Table.Caption>Recent invoices</Table.Caption>\n            <Table.Header>\n                <Table.Row>\n                    <Table.HeaderCell>Invoice</Table.HeaderCell>\n                    <Table.HeaderCell>Status</Table.HeaderCell>\n                    <Table.HeaderCell align=\"right\">Amount</Table.HeaderCell>\n                </Table.Row>\n            </Table.Header>\n            <Table.Body>\n                {invoices.map((inv) => (\n                    <Table.Row key={inv.id}>\n                        <Table.Cell>{inv.id}</Table.Cell>\n                        <Table.Cell>{inv.status}</Table.Cell>\n                        <Table.Cell align=\"right\">{inv.amount}</Table.Cell>\n                    </Table.Row>\n                ))}\n            </Table.Body>\n            <Table.Footer>\n                <Table.Row>\n                    <Table.Cell colSpan={2}>Total</Table.Cell>\n                    <Table.Cell align=\"right\">$750.00</Table.Cell>\n                </Table.Row>\n            </Table.Footer>\n        </Table>\n    );\n}\n```\n\n## Striped rows\n\nPass `striped` to alternate row backgrounds:\n\n```tsx\n<Table striped>\n    {/* ... */}\n</Table>\n```\n\n## Compact\n\nTighter cell padding for dense layouts:\n\n```tsx\n<Table compact>\n    {/* ... */}\n</Table>\n```\n\n## Bordered\n\nDraw a border around all cells:\n\n```tsx\n<Table bordered>\n    {/* ... */}\n</Table>\n```\n\n## Aligned columns\n\n`align` works on both `Table.Cell` and `Table.HeaderCell`:\n\n```tsx\n<Table.HeaderCell align=\"right\">Amount</Table.HeaderCell>\n<Table.Cell align=\"center\">Status</Table.Cell>\n```\n\nAccepted values: `'left'` (default) | `'center'` | `'right'`.\n\n## Column span\n\nPass `colSpan` to merge cells:\n\n```tsx\n<Table.Cell colSpan={2}>Merged cell</Table.Cell>\n```\n\nOn web this maps to the HTML `colspan` attribute. On native it is a visual hint only (the cell still occupies its flex slot).\n\n## Clickable rows\n\nPass `onPress` to `Table.Row` to make a row interactive:\n\n```tsx\n<Table.Row onPress={() => router.push(`/invoices/${inv.id}`)}>\n    {/* ... */}\n</Table.Row>\n```\n\nOn web the row gets a pointer cursor and a hover background. On native it wraps in a `Pressable` with an opacity feedback.\n\n## Native notes\n\n- The root element is a `ScrollView` with `horizontal` enabled so wide tables can scroll on narrow screens.\n- All columns share equal `flex: 1` in v1. Custom column widths are deferred to v2.\n- `striped` is not yet applied on native (rows have no index-aware background without a parent counter context); use a plain list with custom `backgroundColor` on alternating rows as a workaround.\n- `compact` reduces `paddingVertical` on all rows.\n- `bordered` adds a `borderWidth: 1` around the outer container.\n\n## Accessibility\n\n- **Web**: uses semantic table elements. Screen readers announce column headers via `<th>` and can navigate by row/column.\n- **Native**: no explicit `accessibilityRole` is set on the grid container; individual cells are plain `View`s. For data-heavy native UIs where VoiceOver/TalkBack navigation matters, consider labelling cells manually via `accessibilityLabel`.\n\n## API\n\n### `Table`\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `striped` | `boolean` | `false` | Alternate row background tinting. |\n| `compact` | `boolean` | `false` | Reduce cell padding. |\n| `bordered` | `boolean` | `false` | Draw borders around cells. |\n| `className` | `string` | — | Web: extra CSS classes on the `<table>`. |\n| `testID` | `string` | — | Test identifier. |\n\n### `Table.Row`\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `selected` | `boolean` | `false` | Highlight row as selected. |\n| `onPress` | `() => void` | — | Makes the row pressable. |\n| `className` | `string` | — | Web only. |\n| `testID` | `string` | — | Test identifier. |\n\n### `Table.Cell` / `Table.HeaderCell`\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `align` | `'left' \\| 'center' \\| 'right'` | `'left'` | Horizontal alignment. |\n| `colSpan` | `number` | — | Column span (HTML on web; visual on native). |\n| `className` | `string` | — | Web only. |\n| `testID` | `string` | — | Test identifier. |\n\n### `Table.Header` / `Table.Body` / `Table.Footer` / `Table.Caption`\n\nStructural wrappers. Accept `children`, `className` (web), and `testID`.\n"
}
