Data display
DataTable
Sorting, selection, pagination and toolbar, built on TanStack Table.
@codeways/ui/data-table
Sorting, selection, search and pagination
Client-side by default - sort/filter/page state lives internally unless `manual*` props are passed. Search, click a sortable header, or select rows.
| INV-000181 | Lanka Tea Exports | Amara Silva | AUD1,850.00 | Paid | ||
| INV-000182 | Globex Digital | Nadeesha Perera | USD4,200.00 | Paid | ||
| INV-000183 | Colombo Freight Co | Ruwan Jayasuriya | LKR612,500.00 | Overdue by 4 days | ||
| INV-000184 | Sunrise Apparel | Amara Silva | AUD980.00 | Partially paid | ||
| INV-000185 | Acme Pty Ltd | Dilani Fernando | USD3,100.00 | Draft |
Rows per page
1–5 of 12Loading
Skeleton rows, shape-matched to the visible columns - never a spinner.
Empty
First-use empty state when there is no data, a filtered variant when `isFiltered` is set.
No invoices yet
Invoices you send to clients will show up here.
Usage
import { DataTable, type DataTableEmptyOptions } from "@codeways/ui/data-table";
import { Money } from "@codeways/ui/money";
import type { ColumnDef } from "@tanstack/react-table";
const columns: ColumnDef<Invoice, unknown>[] = [
{ accessorKey: "number", header: "Invoice", enableSorting: true },
{
accessorKey: "amount",
header: "Amount",
enableSorting: true,
cell: ({ row }) => <Money amount={row.original.amount} currency={row.original.currency} />,
meta: { align: "right" },
},
];
<DataTable
columns={columns}
data={invoices}
getRowId={(row) => row.id}
enableRowSelection
bulkActions={(selected) => <Button onClick={() => send(selected)}>Send</Button>}
search={q}
onSearchChange={(value) => setParam({ q: value || null })}
empty={{ icon: FileText, title: "No invoices yet", action: <Button variant="primary">New invoice</Button> }}
isFiltered={Boolean(q)}
/>