Table
Headless HTML table for tabular data on TanStack Table, with design-token spacing and class hooks for headers, rows, and cells.
| ID | Name | Role | |
|---|---|---|---|
| 1001 | Maya Adams | Planner | maya.adams@by.com |
| 1002 | Noah Patel | Dispatcher | noah.patel@by.com |
| 1003 | Lina Chen | Analyst | lina.chen@by.com |
| 1004 | Diego Cruz | Manager | diego.cruz@by.com |
Table from @by/table renders a semantic <table> with thead and tbody. You supply data and TanStack columns; density, alignment, and borders are entirely class-driven (className, headerClassName, bodyClassName, rowClassName, cellClassName). The root applies text-base tabular-nums by default so numeric columns align cleanly unless you override it.
Installation
The component is published from @by/table. Add the package with your package manager:
pnpm add @by/tableIn this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/table. Theme tokens and Tailwind utilities such as px-scaled-* assume ThemeProvider from @by/experience-system in the app shell.
Composition
Use the following composition to render a Table:
Table (native table)
├── thead
│ └── tr
│ └── th (one per header; scope col / colgroup)
└── tbody
└── tr (one per row)
└── td (one per visible cell)Table owns the table shell; you define ColumnDef values (often with createColumnHelper) and pass data. Advanced behavior (sorting, filtering, pagination) is optional via the options prop and TanStack row models. See TanStack Table.
Usage
Keep data / columns stable in a small wrapper, and pass layout classes from the parent so designers can tune spacing without touching column definitions.
import { createColumnHelper, Table, type TableProps } from '@by/table';
type Row = { id: number; name: string };
const columnHelper = createColumnHelper<Row>();
const columns = [
columnHelper.accessor('id', { header: 'ID', cell: (info) => info.getValue() }),
columnHelper.accessor('name', { header: 'Name', cell: (info) => info.getValue() }),
];
const data: Row[] = [
{ id: 1, name: 'Ada' },
{ id: 2, name: 'Lin' },
];
type TableViewProps = Omit<TableProps<Row>, 'data' | 'columns' | 'options'>;
export function UserTable(props: TableViewProps) {
return <Table data={data} columns={columns} {...props} />;
}
// Caller
<UserTable
className="w-[760px] border-collapse"
headerClassName="[&_tr_th]:px-scaled-3 [&_tr_th]:py-scaled-2"
bodyClassName="divide-y divide-neutral-alpha-5"
cellClassName="px-scaled-3 py-scaled-2 text-start"
/>Table is a client component ('use client' in the package). Use it inside a Client Component or a dynamic import when using the Next.js App Router.
When to use
Use Table when you need real table semantics and keyboard-friendly reading order, with TanStack’s column model and full control over styling via classes.
When not to use
For non-tabular layouts, use Grid. For loading placeholders, use Skeleton. For very large datasets, add virtualization around rows (for example @tanstack/react-virtual)—not built into Table today.
Examples
Overview
Four-column sample data with border-collapse, row dividers on the body, px-scaled-3 / py-scaled-2 on th via headerClassName, and the same padding on cells. This mirrors TableOverview plus the Table.stories.tsx default args.
| ID | Name | Role | |
|---|---|---|---|
| 1001 | Maya Adams | Planner | maya.adams@by.com |
| 1002 | Noah Patel | Dispatcher | noah.patel@by.com |
| 1003 | Lina Chen | Analyst | lina.chen@by.com |
| 1004 | Diego Cruz | Manager | diego.cruz@by.com |
Styled rows
Two-column shipment list inside a rounded border: zebra rowClassName, column-specific alignment with nth-child selectors on the table, and compact px-scaled-1 padding. Same markup as TableStyled.
| Load ID | Status |
|---|---|
| LD-4191 | On Time |
| LD-4192 | At Risk |
| LD-4193 | Delayed |
| LD-4194 | On Time |
API Reference
Table is implemented in this repo on top of TanStack Table. It forwards native HTML table attributes except where TableProps reserves data for the row array. The package re-exports createColumnHelper, flexRender, common row models, and related types from @tanstack/react-table—see TanStack’s docs for those.
Table
| Prop | Type | Default |
|---|---|---|
data | TData[] | (required) |
columns | ColumnDef<TData, any>[] | (required) |
options | Partial of TanStack TableOptions excluding data, columns, and getCoreRowModel | undefined |
className | string | undefined |
headerClassName | string | undefined |
bodyClassName | string | undefined |
rowClassName | string | ((row: TData, index: number) => string) | undefined |
cellClassName | string | undefined |
The root table merges text-base tabular-nums with your className. thead merges border-b border-neutral-6 with headerClassName; tbody merges divide-y divide-neutral-alpha-5 with bodyClassName. Each th includes font-medium text-left in addition to your header/cell class strategy.
options is merged into the internal useReactTable call with getCoreRowModel always set. Use it for sorting, filtering, pagination, and other table options.
Accessibility
Output is a semantic <table> with <thead> and <tbody>. Header cells use scope="col" (or colgroup when sub-headers exist). Add a caption, visible title, or aria-label when the purpose is not obvious. For interactive headers (sort, filter), supply aria-sort, tabIndex, and keyboard behavior in your column renderers. See MDN: Table accessibility and WAI-ARIA table pattern.
Keyboard interactions
| Key | Description |
|---|---|
Tab / Shift+Tab | Moves focus through focusable elements inside cells in DOM order. |
| Arrow keys | Not handled by Table itself; implement in cell content when building grid or spreadsheet-style interaction. |
Source in the repo: packages/table/src/components/Table.tsx. Agent-oriented contracts: packages/table/src/components/Table.instructions.md.