Table
A configurable data table component with sorting, selection, row actions, and pagination
Usage
import { Table } from '@accelint/design-toolkit';
const columns = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
];
const data = [
{ id: '1', name: 'John Doe', email: 'john@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' },
];
export function MyTable() {
return <Table columns={columns} data={data} enableSorting showCheckbox />;
}Reference
interface TableProps<T extends { id: Key }> {
columns: ColumnDef<T>[];
data: T[];
variant?: 'cozy' | 'compact' | 'crammed';
showCheckbox?: boolean;
rowSelection?: RowSelectionState;
defaultRowSelection?: RowSelectionState;
rowPinning?: RowPinningState;
defaultRowPinning?: RowPinningState;
kebabPosition?: 'left' | 'right';
persistRowKebabMenu?: boolean;
persistHeaderKebabMenu?: boolean;
persistNumerals?: boolean;
enableSorting?: boolean;
enableColumnReordering?: boolean;
enableRowActions?: boolean;
manualSorting?: boolean;
sort?: SortingState;
defaultSort?: SortingState;
fullWidth?: boolean;
pageSize?: number;
page?: number;
defaultPage?: number;
onSortChange?: (sort: SortingState) => void;
onColumnReorderChange?: (index: number) => void;
onRowSelectionChange?: (rowSelection: RowSelectionState) => void;
onRowPinningChange?: (rowPinning: RowPinningState) => void;
onPageChange?: (page: number) => void;
}RowSelectionState, RowPinningState, and SortingState are TanStack Table types. Import them from @tanstack/react-table; the design toolkit does not re-export them.
Props
| Prop | Type | Default | Required |
|---|---|---|---|
columns | ColumnDef<T>[] | - | Yes |
data | T[] | - | Yes |
variant | 'cozy' | 'compact' | 'crammed' | 'cozy' | No |
showCheckbox | boolean | false | No |
rowSelection | RowSelectionState | - | No |
defaultRowSelection | RowSelectionState | {} | No |
rowPinning | RowPinningState | - | No |
defaultRowPinning | RowPinningState | { top: [], bottom: [] } | No |
kebabPosition | 'left' | 'right' | 'right' | No |
persistRowKebabMenu | boolean | true | No |
persistHeaderKebabMenu | boolean | true | No |
persistNumerals | boolean | false | No |
enableSorting | boolean | true | No |
enableColumnReordering | boolean | true | No |
enableRowActions | boolean | true | No |
manualSorting | boolean | false | No |
sort | SortingState | - | No |
defaultSort | SortingState | [] | No |
fullWidth | boolean | false | No |
pageSize | number | - | No |
page | number | - | No |
defaultPage | number | 1 | No |
onSortChange | function | - | No |
onColumnReorderChange | function | - | No |
onRowSelectionChange | function | - | No |
onRowPinningChange | function | - | No |
onPageChange | function | - | No |
columns
Array of TanStack Table column definitions. Each column defines how to access and display data:
{
accessorKey: 'fieldName', // Field to access in data object
header: 'Display Name', // Column header text
cell: ({ row }) => <CustomCell value={row.original.field} />, // Optional custom cell renderer
}data
Array of data objects. Each object must have a unique id property of type string or number.
variant
Visual density variant that controls the padding of header and body cells:
cozy- 12px cell padding, the defaultcompact- 8px cell padding for denser layoutscrammed- 2px cell padding with single-line cells for maximum density
Meta columns (row numerals, selection checkbox, kebab menu) follow the same
padding step, and row height follows the cells. The row and header kebab
menus inherit the density, clamped to the Menu component's cozy | compact
subset, so crammed tables open compact menus.
With fullWidth (fixed layout), crammed truncates text wider than its
column with an ellipsis; set a TanStack column size to control the cutoff.
Custom cell content that intentionally extends beyond the cell edge is also
clipped at crammed; portaled overlays (menus, tooltips) are unaffected.
Only data mode (columns + data) accepts variant. Custom-children mode
renders cozy; wrap the cells in TableContext.Provider to hand-compose a
denser table.
showCheckbox
When true, displays a checkbox column for row selection. The header checkbox selects/deselects all rows.
rowSelection / defaultRowSelection
Controlled and uncontrolled row selection state. An object mapping row IDs to their selection state:
{ 'row-1': true, 'row-2': true } // Rows 1 and 2 are selectedSelection is controlled while rowSelection is provided; pair it with onRowSelectionChange to apply changes, otherwise the selection stays frozen at that value. Use defaultRowSelection to seed an uncontrolled table; it is ignored while rowSelection is provided.
onRowSelectionChange
Called with the plain next RowSelectionState whenever selection changes. Functional updaters from the table engine are resolved internally and never reach this callback.
rowPinning / defaultRowPinning
Controlled and uncontrolled row pinning state: arrays of row IDs pinned to the top and bottom of the table:
{ top: ['row-1'], bottom: [] }Pinning is controlled while rowPinning is provided; pair it with onRowPinningChange to apply changes, otherwise the pinning stays frozen at that value. IDs absent from data are skipped when rendering, never pruned - the controlling owner is responsible for pruning stale IDs.
onRowPinningChange
Called with the plain next RowPinningState when pinning changes (for example via the row kebab menu's Pin / Unpin actions). Functional updaters from the table engine are resolved internally and never reach this callback.
kebabPosition
Controls where the row action menu (kebab menu) appears:
left- Before data columnsright- After data columns (default)
persistRowKebabMenu
When true, the row kebab menu is always visible. When false, only visible on row hover.
persistHeaderKebabMenu
When true, the header kebab menu is always visible. When false, only visible on hover.
persistNumerals
When true, row number column is always visible. When false, only visible on row hover.
enableSorting
Enables column sorting. Use the column header menu (kebab) to sort ascending, sort descending, or clear sorting.
enableColumnReordering
Enables column reordering through the header kebab menu.
enableRowActions
Enables row actions through the kebab menu, including:
- Pin/unpin row
- Move row up/down
manualSorting
When true, assumes data is already sorted (for server-side sorting). Disables client-side sorting; the header menu still updates the sort state and indicators, and onSortChange still receives the plain next SortingState.
sort / defaultSort
Controlled and uncontrolled sort state. An array of { id, desc } entries keyed by column id; the header menu writes at most one entry (no multi-column sort):
[{ id: 'age', desc: true }]Sorting is controlled while sort is provided; pair it with onSortChange to apply changes, otherwise the sort stays frozen at that value. This applies in both client-side and manualSorting modes. Use defaultSort to seed an uncontrolled table; it is ignored while sort is provided.
onSortChange
Called with the plain next SortingState whenever sorting changes: [{ id, desc }], or [] when sorting is cleared. Functional updaters from the table engine are resolved internally and never reach this callback.
fullWidth
When true, applies w-full table-fixed classes for full-width table with fixed layout.
pageSize
Number of rows per page. Enables built-in pagination when set.
page / defaultPage
Controlled and uncontrolled page number (1-indexed). Use page with onPageChange for controlled pagination, or defaultPage for uncontrolled.
Row Actions
The kebab menu provides these actions for each row:
- Pin/Unpin - Pin row to top of table
- Move Up - Move row up one position (disabled for pinned rows and first row)
- Move Down - Move row down one position (disabled for pinned rows and last row)
When rows are selected via checkboxes, move actions apply to all selected rows.
Examples
Example: Basic table with sorting
import { Table } from '@accelint/design-toolkit';
const columns = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'role', header: 'Role' },
{ accessorKey: 'status', header: 'Status' },
];
const data = [
{ id: '1', name: 'Alice', role: 'Engineer', status: 'Active' },
{ id: '2', name: 'Bob', role: 'Designer', status: 'Active' },
{ id: '3', name: 'Carol', role: 'Manager', status: 'Inactive' },
];
<Table columns={columns} data={data} enableSorting />Example: Table with checkboxes and row selection
import { Table } from '@accelint/design-toolkit';
import type { RowSelectionState } from '@tanstack/react-table';
import { useState } from 'react';
export function SelectableTable() {
const [selection, setSelection] = useState<RowSelectionState>({});
const columns = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
];
const data = [
{ id: '1', name: 'John Doe', email: 'john@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' },
];
return (
<Table
columns={columns}
data={data}
showCheckbox
rowSelection={selection}
onRowSelectionChange={setSelection}
/>
);
}Example: Full-width table with pagination
import { Table } from '@accelint/design-toolkit';
import { useState } from 'react';
export function PaginatedTable() {
const [currentPage, setCurrentPage] = useState(1);
const columns = [
{ accessorKey: 'id', header: 'ID' },
{ accessorKey: 'name', header: 'Name' },
];
const data = Array.from({ length: 50 }, (_, i) => ({
id: `${i + 1}`,
name: `Item ${i + 1}`,
}));
return (
<Table
columns={columns}
data={data}
fullWidth
pageSize={10}
page={currentPage}
onPageChange={setCurrentPage}
/>
);
}Example: Server-side sorting
import { Table } from '@accelint/design-toolkit';
import type { SortingState } from '@tanstack/react-table';
import { useState } from 'react';
export function ServerSortedTable() {
const [sort, setSort] = useState<SortingState>([]);
const columns = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'date', header: 'Date' },
];
const handleSortChange = (nextSort: SortingState) => {
setSort(nextSort);
// Fetch sorted data from the server
fetchSortedData(nextSort[0]?.id, nextSort[0]?.desc);
};
return (
<Table
columns={columns}
data={data}
manualSorting
sort={sort}
onSortChange={handleSortChange}
/>
);
}Example: Custom cell renderer
import { Table } from '@accelint/design-toolkit';
import { Badge } from '@accelint/design-toolkit';
const columns = [
{ accessorKey: 'name', header: 'Name' },
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => (
<Badge color={row.original.status === 'active' ? 'success' : 'muted'}>
{row.original.status}
</Badge>
),
},
];
const data = [
{ id: '1', name: 'Project A', status: 'active' },
{ id: '2', name: 'Project B', status: 'inactive' },
];
<Table columns={columns} data={data} />Example: Left-positioned kebab menu
import { Table } from '@accelint/design-toolkit';
<Table
columns={columns}
data={data}
kebabPosition="left"
enableRowActions
/>Example: Minimal row chrome
import { Table } from '@accelint/design-toolkit';
<Table
columns={columns}
data={data}
persistRowKebabMenu={false}
persistNumerals={false}
/>Good to know: When
persistRowKebabMenuandpersistNumeralsarefalse, these elements only appear on row hover, creating a cleaner visual appearance.
Example: Disable specific features
import { Table } from '@accelint/design-toolkit';
<Table
columns={columns}
data={data}
enableSorting={false}
enableColumnReordering={false}
enableRowActions={false}
/>