Quick start

A read-only data table with sorting, selection, pinning, density and CSV export, finished.

Sorting, pinning, CSV export — the table you keep rebuilding, finished.

Every product grows this table eventually: rows the team reads, scans, sorts and exports. Most codebases rebuild it from scratch each time, and each copy drifts. This one is finished and source-shipped, so when a column needs to behave differently you read the code and change it, in node_modules or in your own repo. Every feature sits behind an opt-in prop, so a plain table stays a plain table.

Reference
Client
Role
Status
Amount
PRJ-001
Riverside Studio
Designer
Confirmed
$180
PRJ-002
Northgate Labs
Developer
Pending
$240
PRJ-003
Harbour Media
Copywriter
Confirmed
$150
PRJ-004
Eastfield Group
Designer
Completed
$210
PRJ-005
Southbank Digital
Consultant
Pending
$320

Install

bun add @voila.dev/ui @tanstack/react-table
globals.css
@import "@voila.dev/ui/styles/sources.css";

Or copy the source — it's the same files. Peers: react@19, react-dom@19, tailwindcss@4.

The 3-minute win

Columns in, rows in, a toolbar with export. Sorting is already on for every sortable column.

import { type ColumnDef, DataTable } from "@voila.dev/ui/data-table";
 
const columns: ColumnDef<Project>[] = [
	{ accessorKey: "reference", header: "Reference", size: 130 },
	{ accessorKey: "client", header: "Client" },
	{ accessorKey: "amount", header: "Amount", size: 110 },
];
 
<DataTable.Root
	columns={columns}
	data={projects}
	toolbar={(table) => (
		<DataTable.Toolbar>
			<DataTable.Actions>
				<DataTable.Export table={table} filename="projects.csv" />
			</DataTable.Actions>
		</DataTable.Toolbar>
	)}
/>;

Mental model

The table owns the row model, you own the data. Built on @tanstack/react-table, it takes your columns and rows and derives everything else: sort order, selection state, visibility, pinning, pagination. It never edits a value. If your users need to type into the grid, that is a different component with a different contract; the page Which one do I need? settles it in a minute.

Page map