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@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
- DataTable: the table itself, every extra behind a prop.
- Which one do I need?: read-heavy table or editable grid.
- Selection: a checkbox column and row selection state.
- Column resizing: drag column edges, arrow keys nudge.
- Column pinning: freeze columns against either edge.
- Column visibility: a columns menu via
DataTable.ViewOptions. - Density: comfortable or compact row heights.
- Row expansion: a detail panel under any row.
- Global filter: client-side search across visible cells.
- CSV export:
DataTable.Export, ordataTableToCsvfor the bytes. - Pagination: a server-side windowed footer.