DataTable
A powerful data table with sorting, filtering, pagination, and selection.
$ npx @beaket/ui add data-tableName | Email | Role | Status | |
|---|---|---|---|---|
| Alice Johnson | alice@example.com | Admin | active | |
| Bob Smith | bob@example.com | Editor | active | |
| Carol White | carol@example.com | Viewer | inactive | |
| David Brown | david@example.com | Editor | pending | |
| Eve Davis | eve@example.com | Admin | active |
Showing 1 to 5 of 10 results
Source
import { CircleCheck, CircleMinus, Clock } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { DataTable, type ColumnDef } from "@/components/ui/data-table";
// Sample data type
interface User {
id: string;
name: string;
email: string;
role: string;
status: "active" | "inactive" | "pending";
}
// Sample data
const users: User[] = [
{ id: "1", name: "Alice Johnson", email: "alice@example.com", role: "Admin", status: "active" },
{ id: "2", name: "Bob Smith", email: "bob@example.com", role: "Editor", status: "active" },
{ id: "3", name: "Carol White", email: "carol@example.com", role: "Viewer", status: "inactive" },
{ id: "4", name: "David Brown", email: "david@example.com", role: "Editor", status: "pending" },
{ id: "5", name: "Eve Davis", email: "eve@example.com", role: "Admin", status: "active" },
];
// Column definitions
const columns: ColumnDef<User>[] = [
{
accessorKey: "name",
header: "Name",
size: 180,
},
{
accessorKey: "email",
header: "Email",
size: 220,
},
{
accessorKey: "role",
header: "Role",
size: 120,
},
{
accessorKey: "status",
header: "Status",
size: 120,
cell: ({ row }) => {
const status = row.getValue("status") as User["status"];
const variants = {
active: "default",
inactive: "secondary",
pending: "outline",
} as const;
const icons = { active: CircleCheck, inactive: CircleMinus, pending: Clock } as const;
const Icon = icons[status];
return (
<Badge variant={variants[status]} className="inline-flex w-24 justify-center">
<Icon aria-hidden="true" className="mr-1 size-3" /> {status}
</Badge>
);
},
},
];
const args = {
columns,
data: [
...users,
...users.map((u, i) => ({
...u,
id: `${u.id}-copy-${i}`,
email: `${u.name.toLowerCase().replace(" ", ".")}.${i}@example.com`,
})),
],
searchable: true,
searchPlaceholder: "Search users...",
paginated: true,
pageSize: 5,
className: "[&_td]:!py-0.5",
selectable: true,
onSelectionChange: () => {},
};
export default function Example() {
return <DataTable {...args} />;
}Examples
All Features
Basic Table
Name | Email | Role | Status |
|---|---|---|---|
| Alice Johnson | alice@example.com | Admin | active |
| Bob Smith | bob@example.com | Editor | active |
| Carol White | carol@example.com | Viewer | inactive |
With Search
Name | Email | Role | Status |
|---|---|---|---|
| Alice Johnson | alice@example.com | Admin | active |
| Bob Smith | bob@example.com | Editor | active |
| Carol White | carol@example.com | Viewer | inactive |
With Selection
Name | Email | Role | Status | |
|---|---|---|---|---|
| Alice Johnson | alice@example.com | Admin | active | |
| Bob Smith | bob@example.com | Editor | active | |
| Carol White | carol@example.com | Viewer | inactive |
Compact Mode
Name | Email | Role | Status |
|---|---|---|---|
| Alice Johnson | alice@example.com | Admin | active |
| Bob Smith | bob@example.com | Editor | active |
| Carol White | carol@example.com | Viewer | inactive |
Source
import { CircleCheck, CircleMinus, Clock } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { DataTable, type ColumnDef } from "@/components/ui/data-table";
// Sample data type
interface User {
id: string;
name: string;
email: string;
role: string;
status: "active" | "inactive" | "pending";
}
// Sample data
const users: User[] = [
{ id: "1", name: "Alice Johnson", email: "alice@example.com", role: "Admin", status: "active" },
{ id: "2", name: "Bob Smith", email: "bob@example.com", role: "Editor", status: "active" },
{ id: "3", name: "Carol White", email: "carol@example.com", role: "Viewer", status: "inactive" },
{ id: "4", name: "David Brown", email: "david@example.com", role: "Editor", status: "pending" },
{ id: "5", name: "Eve Davis", email: "eve@example.com", role: "Admin", status: "active" },
];
// Column definitions
const columns: ColumnDef<User>[] = [
{
accessorKey: "name",
header: "Name",
size: 180,
},
{
accessorKey: "email",
header: "Email",
size: 220,
},
{
accessorKey: "role",
header: "Role",
size: 120,
},
{
accessorKey: "status",
header: "Status",
size: 120,
cell: ({ row }) => {
const status = row.getValue("status") as User["status"];
const variants = {
active: "default",
inactive: "secondary",
pending: "outline",
} as const;
const icons = { active: CircleCheck, inactive: CircleMinus, pending: Clock } as const;
const Icon = icons[status];
return (
<Badge variant={variants[status]} className="inline-flex w-24 justify-center">
<Icon aria-hidden="true" className="mr-1 size-3" /> {status}
</Badge>
);
},
},
];
export default () => (
<div className="space-y-8">
<div>
<h3 className="mb-2 text-sm font-medium">Basic Table</h3>
<DataTable columns={columns} data={users.slice(0, 3)} />
</div>
<div>
<h3 className="mb-2 text-sm font-medium">With Search</h3>
<DataTable
columns={columns}
data={users.slice(0, 3)}
searchable
searchPlaceholder="Search..."
/>
</div>
<div>
<h3 className="mb-2 text-sm font-medium">With Selection</h3>
<DataTable columns={columns} data={users.slice(0, 3)} selectable />
</div>
<div>
<h3 className="mb-2 text-sm font-medium">Compact Mode</h3>
<DataTable columns={columns} data={users.slice(0, 3)} compact />
</div>
</div>
);Props
| Name | Type | Default | Description |
|---|---|---|---|
| labels | DataTableLabels | undefined | — | Accessible names and pagination summary; omitted labels retain English defaults. |
| columns* | ColumnDef<TData, unknown>[] | — | Column definitions using TanStack Table's ColumnDef — see https://tanstack.com/table/latest/docs/guide/column-defs |
| data* | TData[] | — | Array of data to display |
| searchable | boolean | undefined | false | Enable global search/filter functionality |
| searchPlaceholder | string | undefined | Search... | Search placeholder text |
| paginated | boolean | undefined | false | Enable pagination |
| pageSize | number | undefined | 10 | Number of rows per page |
| emptyMessage | string | undefined | No data available | Empty state message |
| emptyState | ReactNode | — | Custom empty state component |
| enableSorting | boolean | undefined | true | Enable column sorting (default: true) |
| enableFiltering | boolean | undefined | true | Enable column filtering (default: true) |
| initialColumnVisibility | ColumnVisibilityState | undefined | — | Initial column visibility state |
| compact | boolean | undefined | false | Show compact table styling |
| selectable | boolean | undefined | false | Enable row selection with checkboxes |
| onSelectionChange | ((selectedRows: TData[]) => void) | undefined | — | Callback when row selection changes |
| initialRowSelection | RowSelectionState | undefined | — | Initial row selection state |
| getRowClassName | ((row: TData) => string) | undefined | — | Function to generate custom class names for each row based on row data |
| onRowMouseEnter | ((row: TData) => void) | undefined | — | Callback when mouse enters a row |
| onRowMouseLeave | ((row: TData) => void) | undefined | — | Callback when mouse leaves a row |
| children | ReactNode | ((table: DataTableInstance<TData>) => ReactNode) | — | Arrange the parts yourself instead of letting the props lay them out. A function child receives the TanStack table instance (§11). |
| Toolbar.searchPlaceholder | string | Search... | Placeholder for the default search field |
| Row.row* | Row<TData> | — | The row this cell group renders. Cells are derived from it. |
| Empty.message | string | No data available | Message shown when no children are supplied |