Data Display/Data Table

Data Table

Powerful table with sorting, filtering and pagination.

Themed

When to Use

Use Data Table when you need to:

  • Display structured tabular data with interactive sorting and filtering
  • Enable users to search, paginate, and navigate large datasets
  • Support row selection for bulk actions (delete, export, assign)
  • Provide column visibility controls so users can customize their view
  • Format cells with custom renderers (currency, status badges, actions)

When Not to Use

  • Simple static data with no interactivity - use Table
  • Key-value pairs or metadata - use a description list or Card
  • Small lists of items (fewer than 5) - use Card or a simple list
  • Highly visual or media-rich content - use a grid of Cards

Full Featured

Data table with sorting, filtering, pagination, row selection, and column visibility.

Status
Amount
success
ken99@example.com
$316.00
success
abe45@example.com
$242.00
processing
monserrat44@example.com
$837.00
success
silas22@example.com
$874.00
failed
carmella@example.com
$721.00
pending
jessica@example.com
$456.00
success
michael@example.com
$623.00
processing
sarah@example.com
$198.00
0 of 8 row(s) selected.

Basic Table

Simple data table using TanStack Table with column definitions, accessors, and custom cell renderers.

StatusEmail
Amount
success
ken99@example.com
$316.00
success
abe45@example.com
$242.00
processing
monserrat44@example.com
$837.00
success
silas22@example.com
$874.00
failed
carmella@example.com
$721.00

Sorting

Click column headers to toggle ascending and descending sort order.

success
ken99@example.com
$316.00
success
abe45@example.com
$242.00
processing
monserrat44@example.com
$837.00
success
silas22@example.com
$874.00
failed
carmella@example.com
$721.00

Filtering

Filter table data by column values using a controlled text input.

StatusEmail
Amount
success
ken99@example.com
$316.00
success
abe45@example.com
$242.00
processing
monserrat44@example.com
$837.00
success
silas22@example.com
$874.00
failed
carmella@example.com
$721.00
pending
jessica@example.com
$456.00
success
michael@example.com
$623.00
processing
sarah@example.com
$198.00

Pagination

Navigate through pages of data with previous and next controls.

StatusEmail
Amount
success
ken99@example.com
$316.00
success
abe45@example.com
$242.00
processing
monserrat44@example.com
$837.00
Page 1 of 3

Row Selection

Select individual rows or all rows on the current page with checkboxes.

StatusEmail
Amount
success
ken99@example.com
$316.00
success
abe45@example.com
$242.00
processing
monserrat44@example.com
$837.00
success
silas22@example.com
$874.00
failed
carmella@example.com
$721.00
0 of 5 row(s) selected.

UX & Design Guidelines

Data Density & Readability

Use consistent padding within cells (px-4 py-2) and align numeric values to the right. Keep column count manageable (5-8 visible columns) and use column visibility toggles for optional fields. Alternate row styling with data-[state=selected]:bg-muted helps users track across wide tables.

Sorting & Filtering Patterns

Place filter inputs above the table, aligned with the column they control. Use variant="ghost" buttons in sortable column headers so they appear interactive without visual clutter. Show clear sort direction indicators (up/down arrows) so users always know the current sort state.

Responsive Behavior

Wrap the table in a horizontally scrollable container on small screens. Hide less-important columns by default on mobile using column visibility state. Consider switching to a stacked card layout below md breakpoint for complex tables. Pagination controls should remain accessible at all viewport sizes.

Empty & Loading States

Always provide a clear empty state message when no rows match the current filter (e.g., "No results found"). Use colSpan to span the full table width for the empty message. Show Skeleton rows during initial data loading to communicate layout structure.

Accessibility

Keyboard Navigation

  • Tab -- Move focus between interactive elements (filters, sort buttons, checkboxes, pagination)
  • Enter or Space -- Activate sort buttons, toggle checkboxes, or navigate pages
  • Arrow Up / Arrow Down -- Navigate within dropdown menus (column visibility)
  • Escape -- Close open dropdown menus or popovers

Screen Reader Support

  • The native <table> element provides implicit grid semantics for screen readers
  • Add aria-label to all selection checkboxes (e.g., "Select row", "Select all")
  • Use aria-sort="ascending" or "descending" on sortable column headers
  • Announce filter results and page changes with aria-live="polite" regions

Focus Management

Sort buttons and checkboxes display a visible focus ring using focus-visible:ring-[3px]. After toggling sort order, focus remains on the column header button. When navigating pages, focus should return to the first interactive element in the table or the pagination controls.

Row Selection Patterns

The "Select all" checkbox uses an indeterminate state when only some rows are selected. Each row checkbox includes a descriptive aria-label. Selected row count is displayed as live text so screen readers announce changes. Selection columns use enableSorting: false and enableHiding: false to keep them always visible and non-sortable.

ARIA Attributes

{/* Table with accessible caption */}
<Table>
  <caption className="sr-only">Payment transactions</caption>
  <TableHeader>...</TableHeader>
  <TableBody>...</TableBody>
</Table>

{/* Row selection with labeled checkboxes */}
<Checkbox
  checked={row.getIsSelected()}
  onCheckedChange={(value) => row.toggleSelected(!!value)}
  aria-label={`Select row ${row.index + 1}`}
/>

{/* Sortable header with aria-sort */}
<TableHead aria-sort={
  column.getIsSorted() === "asc" ? "ascending"
    : column.getIsSorted() === "desc" ? "descending"
    : "none"
}>
  <Button variant="ghost" onClick={() => column.toggleSorting()}>
    Email <ArrowUpDown className="ml-2 h-4 w-4" />
  </Button>
</TableHead>

{/* Pagination with live region */}
<div aria-live="polite" className="text-sm text-muted-foreground">
  Page {table.getState().pagination.pageIndex + 1} of{" "}
  {table.getPageCount()}
</div>

API Reference

The Data Table is a composition pattern built with @tanstack/react-table. The following are the primary options passed to useReactTable.

PropTypeDefaultDescription
dataTData[]Array of data objects to display as table rows.
columnsColumnDef<TData>[]Column definitions including accessors, headers, and cell renderers.
getCoreRowModel() => CoreRowModelRequired. Returns the core row model for the table.
getSortedRowModel() => SortedRowModelundefinedEnables client-side sorting. Import from @tanstack/react-table.
getFilteredRowModel() => FilteredRowModelundefinedEnables client-side column filtering. Import from @tanstack/react-table.
getPaginationRowModel() => PaginationRowModelundefinedEnables client-side pagination. Import from @tanstack/react-table.
statePartial<TableState>{}Controlled state object for sorting, filtering, pagination, row selection, and column visibility.
onSortingChangeOnChangeFn<SortingState>undefinedCallback fired when the sorting state changes.
onColumnFiltersChangeOnChangeFn<ColumnFiltersState>undefinedCallback fired when column filters change.
onRowSelectionChangeOnChangeFn<RowSelectionState>undefinedCallback fired when row selection changes.

ColumnDef Reference

Column definitions describe how each column accesses, renders, and behaves within the table.

PropTypeDefaultDescription
accessorKeystringKey to access the value from each data row object.
headerstring | HeaderFunctionColumn header content. Can be a string or render function for custom headers.
cellCellFunctionautoCell render function with access to row data via row.getValue() and row.original.
enableSortingbooleantrueWhether the column supports sorting when getSortedRowModel is enabled.
enableHidingbooleantrueWhether the column can be toggled via column visibility controls.
idstringaccessorKeyUnique identifier for the column. Required for non-accessor columns like selection.