Data Table
A client-side table over data you already hold. Sorting, paging, column visibility and search all run in the browser.
'use client'
import * as React from 'react'
import type { ColumnDef } from '@tanstack/react-table'
import { DataTable } from '@/components/custom/data-table'
import { StatusBadge, type PaymentStatus } from '@/components/custom/status-badge'
import { formatMoney } from '@/lib/kiota-format'
interface Payout {
reference: string
beneficiaryName: string
amount: number
status: PaymentStatus
}
const PAYOUTS: Payout[] = [
{ reference: 'PO-1041', beneficiaryName: 'Ada Lovelace', amount: 125050, status: 'success' },
{ reference: 'PO-1042', beneficiaryName: 'Grace Hopper', amount: 84000, status: 'pending' },
{ reference: 'PO-1043', beneficiaryName: 'Wanjiru Kamau', amount: 231575, status: 'failed' },
{ reference: 'PO-1044', beneficiaryName: 'Otieno Odhiambo', amount: 45900, status: 'refunded' },
{ reference: 'PO-1045', beneficiaryName: 'Amina Yusuf', amount: 660000, status: 'success' },
]
const columns: ColumnDef<Payout>[] = [
{ accessorKey: 'reference', header: 'Reference' },
{ accessorKey: 'beneficiaryName', header: 'Beneficiary' },
{
accessorKey: 'amount',
header: 'Amount',
cell: ({ row }) => <span className="tabular-nums">{formatMoney(row.original.amount)}</span>,
},
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => <StatusBadge status={row.original.status} />,
},
]
export default function DataTableDemo() {
return (
<div className="w-full">
<DataTable
columns={columns}
data={PAYOUTS}
searchKeys={['reference', 'beneficiaryName']}
sortField="reference"
tableClassName="min-w-[640px]"
/>
</div>
)
}Installation
pnpm dlx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/data-table.jsonnpx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/data-table.jsonyarn dlx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/data-table.jsonbunx --bun shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/data-table.jsonInstalls data-table.tsx and data-table-pagination.tsx, plus the shadcn table, button, select and dropdown-menu primitives. Adds @tanstack/react-table.
Usage
'use client'
import type { ColumnDef } from '@tanstack/react-table'
import { DataTable } from '@/components/custom/data-table'
const columns: ColumnDef<Payout>[] = [
{ accessorKey: 'reference', header: 'Reference' },
{ accessorKey: 'beneficiaryName', header: 'Beneficiary' },
]
export function PayoutTable({ payouts, isLoading }) {
return (
<DataTable
columns={columns}
data={payouts}
searchKeys={['reference', 'beneficiaryName']}
sortField="reference"
isLoading={isLoading}
/>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
columns | ColumnDef<TData, TValue>[] | — | Required. Standard TanStack column defs. |
data | TData[] | — | Required. null and undefined are treated as empty. |
searchKeys | string[] | — | Column ids the search box looks through. Supports dotted paths. |
toolbar | ReactNode | — | Replaces the built-in search box. |
isLoading | boolean | false | Shows a progress bar without hiding current rows. |
sortField | string | — | Column id to sort by on first render. |
sortDesc | boolean | false | Direction for sortField. |
tableClassName | string | — | Applied to the <Table>; override the 1200px minimum here. |
Right for a few hundred rows arriving in one response. Past that, the whole set has to be shipped and held in memory to render one page of it — use Paginated Data Table instead.
Behaviour notes
A # column is prepended automatically, numbered by position in the rendered rows, so the sequence reads 1..N down the page regardless of how the user has sorted.
Search runs over searchKeys, not columns. That is what lets a key name a nested path — 'branch.name' — which no single column owns. It is a case-insensitive substring match.
Sortable headers are buttons, so they are reachable by keyboard and announced as activatable rather than being click-only divs.
Grid view is the same rows, restacked as cards for narrow screens, with column ids humanised into labels.