← Back to home

Table

A fully-featured data table component powered by TanStack Table.

Requires: npm install @tanstack/react-table

Usage

tsx
import { useState } from 'react';
import { Pagination, Table } from '@components-kit/react';
import type { ColumnDef, RowSelectionState, SortingState } from '@components-kit/react';

interface User {
  id: string;
  name: string;
  email: string;
}

const columns: ColumnDef<User>[] = [
  { accessorKey: 'name', header: 'Name' },
  { accessorKey: 'email', header: 'Email' },
];

const users: User[] = [
  { id: '1', name: 'Jane Doe', email: 'jane@example.com' },
  { id: '2', name: 'John Smith', email: 'john@example.com' },
];

// Basic usage
<Table data={users} columns={columns} />

// With row selection
function SelectableUsersTable() {
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

  return (
    <Table
      data={users}
      columns={columns}
      enableRowSelection
      rowSelection={rowSelection}
      onRowSelectionChange={setRowSelection}
    />
  );
}

// With sorting + pagination UI
function SortablePaginatedUsersTable() {
  const [sorting, setSorting] = useState<SortingState>([]);
  const [pageIndex, setPageIndex] = useState(0);
  const pageSize = 10;

  return (
    <>
      <Table
        data={users}
        columns={columns}
        enableSorting
        sorting={sorting}
        onSortingChange={setSorting}
        enablePagination
        pageIndex={pageIndex}
        pageSize={pageSize}
        onPageChange={setPageIndex}
      />
      <Pagination
        page={pageIndex + 1}
        totalPages={Math.ceil(users.length / pageSize)}
        onPageChange={(page) => setPageIndex(page - 1)}
      />
    </>
  );
}

// Custom cell rendering
<Table
  data={users}
  columns={[
    { accessorKey: 'name', header: 'Name' },
    {
      accessorKey: 'status',
      header: 'Status',
      cell: ({ getValue }) => (
        <span data-status={getValue() as string}>{getValue() as string}</span>
      ),
    },
  ]}
/>

// With column footers
<Table
  data={users}
  columns={[
    { accessorKey: 'name', header: 'Name', footer: 'Total' },
    { accessorKey: 'email', header: 'Email' },
    { accessorKey: 'amount', header: 'Amount', footer: ({ table }) => {
      const total = table.getRowModel().rows.reduce(
        (sum, row) => sum + row.getValue<number>('amount'), 0
      );
      return `$${total}`;
    }},
  ]}
/>

// With row expansion
<Table
  data={users}
  columns={columns}
  enableExpanding
  renderSubComponent={({ row }) => (
    <div>Details for {row.original.name}</div>
  )}
/>

Props

Core

PropTypeDefaultDescription
dataTData[]requiredData array to display
columnsColumnDef<TData>[]requiredColumn definitions
captionReactNode-Table caption content
captionSide"top" | "bottom""top"Caption position
isLoadingbooleanfalseShows loading state row
variantNameVariantFor<"table">-Variant name for styling
aria-labelstring-Accessible label for the table
aria-describedbystring-ID of an element describing the table

Data / Identity

PropTypeDefaultDescription
getRowId(originalRow, index) => string-Custom row ID extraction
getSubRows(originalRow) => TData[] | undefined-Returns nested rows for hierarchical data

Sorting

PropTypeDefaultDescription
enableSortingbooleanfalseEnable column sorting
sortingSortingState-Controlled sorting state
onSortingChange(sorting) => void-Sorting change callback
manualSortingbooleanfalseServer-side sorting

Filtering

PropTypeDefaultDescription
enableFilteringbooleanfalseEnable filtering features
enableColumnFiltersbooleanfalseEnable per-column filters
globalFilterstring-Controlled global filter value
columnFiltersColumnFiltersState-Controlled column filters
onGlobalFilterChange(globalFilter: string) => void-Global filter change callback
onColumnFiltersChange(columnFilters: ColumnFiltersState) => void-Column filters change callback
manualFilteringbooleanfalseServer-side filtering

Pagination

The Table handles pagination data logic (row slicing) but does not render pagination UI. Compose with the Pagination component for navigation controls.

PropTypeDefaultDescription
enablePaginationbooleanfalseEnable pagination (row slicing)
pageSizenumber10Rows per page
pageIndexnumber0Current page (0-indexed)
pageCountnumber-Total pages (manual pagination)
onPageChange(index) => void-Page change callback
onPageSizeChange(size) => void-Page size change callback
manualPaginationbooleanfalseServer-side pagination

Row Selection

PropTypeDefaultDescription
enableRowSelectionboolean | (row) => booleanfalseEnable selection
enableMultiRowSelectionbooleantrueAllow multi-select
rowSelectionRowSelectionState-Controlled selection state
onRowSelectionChange(selection) => void-Selection change callback
onRowClick(row) => void-Row click callback

Footer

The table footer renders automatically when any column definition includes a footer property. No enable prop needed.

PropTypeDefaultDescription
renderFooter({ table }) => ReactNode-Custom footer renderer (overrides column footers)

Row Expansion

PropTypeDefaultDescription
enableExpandingbooleanfalseEnable row expansion
expandedExpandedState-Controlled expanded state
onExpandedChange(expanded) => void-Expansion change callback
renderSubComponent({ row }) => ReactNode-Expanded content renderer

Custom Renderers

PropTypeDefaultDescription
renderHeader({ table }) => ReactNode-Custom header renderer
renderCell({ cell }) => ReactNode-Custom cell renderer
renderRow({ row, index }) => ReactNode-Custom row renderer
renderLoading() => ReactNode-Custom loading row renderer
renderEmpty() => ReactNode-Custom empty row renderer

Data Attributes

AttributeValuesDescription
data-variantstringVariant name for styling
data-caption-side"top", "bottom"Caption position
data-clickabletrueRow has click handler
data-loadingtruePresent when loading
data-sort"asc", "desc", "none"Column sort direction
data-sortabletrueColumn is sortable
data-selectedtrueRow is selected
data-footer-rowpresentRow is a footer row

Accessibility

  • Proper aria-sort on sortable column headers
  • aria-selected on selectable rows
  • Keyboard navigation for sorting (Enter/Space on headers)

Best Practices

  • Provide aria-label or caption for table context
  • Use semantic column headers
  • Ensure sufficient color contrast
  • Consider keyboard-only users for all interactions

Type Exports

Re-exports from TanStack Table for convenience:

tsx
import type {
  Cell,
  ColumnDef,
  ColumnFiltersState,
  ExpandedState,
  Row,
  RowSelectionState,
  SortingState,
  TableInstance,
} from "@components-kit/react";