Standard Toolkit
Toolkits@accelint/design-toolkitComponents

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

PropTypeDefaultRequired
columnsColumnDef<T>[]-Yes
dataT[]-Yes
variant'cozy' | 'compact' | 'crammed''cozy'No
showCheckboxbooleanfalseNo
rowSelectionRowSelectionState-No
defaultRowSelectionRowSelectionState{}No
rowPinningRowPinningState-No
defaultRowPinningRowPinningState{ top: [], bottom: [] }No
kebabPosition'left' | 'right''right'No
persistRowKebabMenubooleantrueNo
persistHeaderKebabMenubooleantrueNo
persistNumeralsbooleanfalseNo
enableSortingbooleantrueNo
enableColumnReorderingbooleantrueNo
enableRowActionsbooleantrueNo
manualSortingbooleanfalseNo
sortSortingState-No
defaultSortSortingState[]No
fullWidthbooleanfalseNo
pageSizenumber-No
pagenumber-No
defaultPagenumber1No
onSortChangefunction-No
onColumnReorderChangefunction-No
onRowSelectionChangefunction-No
onRowPinningChangefunction-No
onPageChangefunction-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 default
  • compact - 8px cell padding for denser layouts
  • crammed - 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 selected

Selection 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 columns
  • right - 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 persistRowKebabMenu and persistNumerals are false, 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}
/>
  • Button - Used for row action triggers
  • Checkbox - Used for row selection
  • Menu - Used for row and column actions
  • Icon - Used for action icons

On this page