Table

A theme-aware data grid: sort, filter, search, select, edit, expand, paginate, pin, resize and more.

v2.13.0·Updated Jul 11, 2026 · 22:00
A data grid, not just a table

Table is the workhorse for rows of comparable records: scan, sort, filter, search, select, edit in place and drill into a row. Every feature is opt-in, so the same component covers a three-column read-only list and a full editable grid. It reads its colours from tokens, so it follows the theme on the page. For a vertical stack of single ideas reach for a List; for quick filters use Pills.

A simple table
Bank nameSectorDoc. categoryDoc Size
ClearView ComplianceValley NationalFinanceCertificate₹7,62,43212.3 MB
EverGreen Audit PartnersSunriseTransportationFinancial Statements₹7,38,76240 MB
BlueSky Financial ReviewsSilverWaveHealthcarePolicies₹1,23,240192 KB
Integrity Audit SolutionsRiverStone ComPharmaceuticalsTraining Manuals₹43,600120 KB
GoldLeaf Risk AdvisorsPinnacle Trust B.HospitalityTax Filings₹45,6808 MB
SummitPoint AssuranceMaple RidgeAgricultureProject Plans₹49,150540 KB
The whole component from just { columns, data }: sort a header, and that is it. Everything else is opt-in.
Filter with removable tags
Bank nameSectorDoc. category
ClearView ComplianceValley NationalFinanceCertificate₹7,62,432
EverGreen Audit PartnersSunriseTransportationFinancial Statements₹7,38,762
BlueSky Financial ReviewsSilverWaveHealthcarePolicies₹1,23,240
Integrity Audit SolutionsRiverStone ComPharmaceuticalsTraining Manuals₹43,600
GoldLeaf Risk AdvisorsPinnacle Trust B.HospitalityTax Filings₹45,680
SummitPoint AssuranceMaple RidgeAgricultureProject Plans₹49,150
TrueNorth AuditHorizon BankFinanceHR Records₹10,520
Meridian ControlsValley NationalTransportationCertificate₹5,22,090
Total 25 items
Click the funnel on Bank name, Sector or Doc. category and pick a value. Each selection appears as a removable tag above the table.
Global filter bar
25 Data Subject Tickets
Bank nameSectorDoc. category
ClearView ComplianceValley NationalFinanceCertificate₹7,62,432
EverGreen Audit PartnersSunriseTransportationFinancial Statements₹7,38,762
BlueSky Financial ReviewsSilverWaveHealthcarePolicies₹1,23,240
Integrity Audit SolutionsRiverStone ComPharmaceuticalsTraining Manuals₹43,600
GoldLeaf Risk AdvisorsPinnacle Trust B.HospitalityTax Filings₹45,680
SummitPoint AssuranceMaple RidgeAgricultureProject Plans₹49,150
TrueNorth AuditHorizon BankFinanceHR Records₹10,520
Meridian ControlsValley NationalTransportationCertificate₹5,22,090
Total 25 items
A filter bar above the table: a search, quick-filter dropdowns per column, and All filters. Filtering is instant.
The full data grid · Fully Interactive
2340 Data Subject Tickets
AuditorFilingTotals
Bank nameSectorDoc. categoryFirm Reg. numberDoc Size
ClearView ComplianceValley NationalFinanceCertificateREG-1023429 Jun 2022₹7,62,43212.3 MB
EverGreen Audit PartnersSunriseTransportationFinancial StatementsREG-1060715 Mar 2024₹7,38,76240 MB
BlueSky Financial ReviewsSilverWaveHealthcarePoliciesREG-1098019 Oct 2022₹1,23,240192 KB
Integrity Audit SolutionsRiverStone ComPharmaceuticalsTraining ManualsREG-1135303 Mar 2020₹43,600120 KB
GoldLeaf Risk AdvisorsPinnacle Trust B.HospitalityTax FilingsREG-1172622 Aug 2023₹45,6808 MB
SummitPoint AssuranceMaple RidgeAgricultureProject PlansREG-1209911 Jan 2023₹49,150540 KB
TrueNorth AuditHorizon BankFinanceHR RecordsREG-1247207 Sep 2021₹10,5202.1 MB
Meridian ControlsValley NationalTransportationCertificateREG-1284518 Feb 2024₹5,22,09076 KB
Anchor AssuranceSunriseHealthcarePoliciesREG-1321829 Jun 2022₹2,20,960290 KB
Northgate Audit CoSilverWavePharmaceuticalsLegal DocumentsREG-1359115 Mar 2024₹8,92,33012 MB
Total 25 items
Open Table Features to switch capabilities on and off, try the Advanced Filter (build a query like “Name does not equal …”), expand a row, scroll sideways, or change rows-per-page.
Rows & cell types

A cell renders whatever you return from column.render. Switch to the Cells tab to see rows built from our own components: an Avatar and Avatar group, a status Badge, an Indicator dot, Tags, a star rating, a Progress bar, a sparkline, a Toggle, a Link, and a Button plus an icon Button.

OwnerStatusProgressPlan
GreenLeaf FinancialIvy LaneActive82%Enterprise
Pro PropertyRavi KumarPending47%Growth
Nova RetailMia OnoActive66%Growth
Alro KropertySam LeePaused23%Starter
What it does
FeatureBehaviour
SortSortable columns become header buttons. Click cycles ascending, descending, then off; numeric columns sort numerically.
FilterA funnel on filterable columns opens a menu of the distinct values. Tick any to narrow the rows; Clear resets.
SearchA single toolbar box filters across every column at once.
SelectA leading checkbox column with a header select-all and an indeterminate state for a partial selection.
EditClick a row to edit every editable cell at once, inline; a bar offers Save or Discard, and validation blocks a bad save.
Column menuA kebab on each header opens sort, pin (left/right/none), autosize, autosize-all, group by, choose columns and reset.
GroupsColumns that share a group render a spanning second-level header above them.
QueryThe advanced filter builds column / operator / value conditions (equals, does not equal, contains, greater than …) matched all or any.
ValidateA column validator flags a bad cell with a red border and X, tints the row, and counts into the toolbar chip.
ExpandrenderDetail adds a disclosure column; expanding a row reveals a panel below it, often a nested table.
PaginatepageSize splits the rows and shows a compact pager with the total count.
PinFreeze a column to the left or right, and pin summary rows to the top or bottom, so key data stays in view while the rest scrolls.
ResizeDrag a header's right edge to widen or narrow a column; the frozen columns and their offsets keep up.
RefineRow numbers, floating per-column filters, a column-hover tint, a status bar and right-to-left layout, each an independent flag.
Variants

Two orthogonal knobs shape the grid without touching its features. Flip them live with the controls above the demo.

PropOptionsWhat changes
densitycompact · default · comfortableTightens or opens up the row height for dense tables or airy ones. Header and body move together.
appearanceline · grid · zebraline keeps only row hairlines, grid draws every cell border for spreadsheet-style data, zebra stripes alternate rows and drops the hairline.
borderlessbooleanRemoves the outer border and radius so the grid sits flush inside a surface you already frame, like a card or a panel.
tsx
<Table columns={columns} data={rows} density="compact" appearance="grid" />
<Table columns={columns} data={rows} appearance="zebra" />
<Table columns={columns} data={rows} borderless />
Behavior · The Engineering Contract
PropTypeNotes
columnsColumn<T>[]Per column: key, header, align, width, sortable, filterable, value, render, editable, editor, options, validate
dataT[]The rows, each keyed by the column keys
title / actionsReactNodeToolbar left title and right-side slot for buttons
searchablebooleanAdds the global search box to the toolbar
selectablebooleanAdds the checkbox column and header select-all
selected / onSelectedChangestring[] · (ids) => voidControlled selection; omit both for uncontrolled
editablebooleanMaster switch; columns opt in with editable + editor. Editing a cell opens the whole row inline with Save / Discard
onCellEdit(rowId, key, value) => voidFires per cell when a row's edits are saved
renderDetail(row) => ReactNodeEnables expandable master/detail rows
pageSizenumberRows per page. Omit to render every row
pageSizeOptionsnumber[]Rows-per-page choices shown as a footer selector, e.g. [5, 10, 20]
advancedFilterbooleanToolbar query builder: [column] [operator] value, matched all or any
columns[].groupstringAdjacent columns sharing it get a spanning group header
columns[].pinnedleft | rightFreeze that column against an edge on horizontal scroll
pinnedRows{ top?: T[]; bottom?: T[] }Rows frozen top / bottom, outside sort, filter and paging
resizablebooleanDrag a header's right edge to resize the column
rowNumbersbooleanA leading 1-based index column
floatingFiltersbooleanA per-column filter row: text columns get a checkbox value list, number columns add a range slider
columns[].typetext | numberDrives the floating filter's control; auto-inferred from the data if omitted
columnHoverbooleanTint the whole column under the cursor
statusBarbooleanA footer strip with the row and selection counts
rtlbooleanLay the grid out right-to-left
densitycompact | default | comfortableRow height
appearanceline | grid | zebraRow separators: hairlines, full cell borders, or stripes
borderlessbooleanDrop the outer frame for full-bleed embedding
getRowId / stickyHeader(row, i) => string · booleanStable ids; pin the header while the body scrolls
Theme & Tokens

Nothing in the grid is a hard-coded colour. It reads these token roles, so switching data-theme on the page recolours the whole grid, dark to daylight, with no prop changes. Try the toggle in the demo above.

Token roleWhere it lands
--surface / --surface-raisedGrid field and the header / hover fill
--border / --border-subtleOuter border and the hairline between rows
--accent / --on-accentActive sort, current page, selection tint
--danger-solid / --danger-surfaceCell error border, X icon and the row tint
--warning-surface / --warning-textThe toolbar error chip
--font-monoHeader labels and numeric cells
--radius-12 / --shadow-2Panel radius and the filter / editor menus
Code
import { Table, type Column } from "./ui/components/table/table";

type Row = { id: string; name: string; sector: string; category: string; seats: number };

const columns: Column<Row>[] = [
  { key: "name", header: "Name", sortable: true },
  { key: "sector", header: "Sector", filterable: true },
  {
    key: "category", header: "Category",
    editable: true, editor: "select", options: CATEGORIES,
    validate: (v) => (v === "Policies" ? "Not an allowed category" : null),
  },
  { key: "seats", header: "Seats", align: "right", sortable: true },
];

<Table
  columns={columns}
  data={rows}
  getRowId={(r) => r.id}
  title="Data subjects"
  searchable
  selectable
  editable
  onCellEdit={(id, key, value) => save(id, key, value)}
  renderDetail={(r) => <ChargesTable owner={r.id} />}
  pageSize={10}
/>