DataTable
An advanced data table component with sorting, filtering, pagination, and more.
Import
import { DataTable } from '@/components/ui/data-table/DataTable';
Basic Usage
import { DataTable } from '@/components/ui/data-table/DataTable';
import type { ColumnDef } from '@tanstack/react-table';
interface User {
id: string;
name: string;
email: string;
}
const columns: ColumnDef<User>[] = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
];
<DataTable columns={columns} data={users} />
Props
| Prop | Type | Default | Description |
|---|---|---|---|
columns |
ColumnDef<TData>[] |
required | Column definitions |
data |
TData[] |
required | Table data |
mode |
'client' | 'server' |
'client' |
Pagination mode |
pagination |
{ page, pageSize, totalRows } |
- | Server pagination state |
onStateChange |
(state) => void |
- | State change callback |
initialState |
TableState |
- | Initial table state |
pageSizeOptions |
number[] |
[20, 50, 100, 200, 500] |
Page size options |
enableGlobalFilter |
boolean |
true |
Enable search |
enableFilterBuilder |
boolean |
true |
Enable column filters |
isLoading |
boolean |
false |
Show loading skeleton |
isFetching |
boolean |
false |
Show fetching indicator |
emptyMessage |
ReactNode |
- | Custom empty state |
toolbar |
ReactNode |
- | Custom toolbar content |
onRowClick |
(row) => void |
- | Row click handler |
getRowId |
(row) => string |
- | Custom row ID |
getRowCanExpand |
(row) => boolean |
- | Expandable rows |
renderSubComponent |
(row) => ReactNode |
- | Expanded row content |
Column Definition
const columns: ColumnDef<User>[] = [
// Basic column
{ accessorKey: 'name', header: 'Name' },
// With custom cell
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => (
<Badge variant={row.original.status === 'active' ? 'success' : 'default'}>
{row.original.status}
</Badge>
),
},
// With column meta
{
accessorKey: 'email',
header: 'Email',
meta: {
filterable: true,
filterType: 'text',
},
},
// With size
{
accessorKey: 'actions',
header: '',
size: 50,
cell: ({ row }) => <ActionMenu id={row.original.id} />,
},
];
Client-Side Pagination
<DataTable
columns={columns}
data={users}
mode="client"
enableGlobalFilter
enableFilterBuilder
/>
Server-Side Pagination
'use client';
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { DataTable } from '@/components/ui/data-table/DataTable';
export function UsersTable() {
const [tableState, setTableState] = useState({
pagination: { pageIndex: 0, pageSize: 20 },
globalFilter: '',
columnFilters: [],
sorting: [],
});
const { data, isLoading, isFetching } = useQuery({
queryKey: ['users', tableState],
queryFn: () => fetchUsers({
page: tableState.pagination.pageIndex + 1,
pageSize: tableState.pagination.pageSize,
search: tableState.globalFilter,
filters: tableState.columnFilters,
sort: tableState.sorting,
}),
});
return (
<DataTable
columns={columns}
data={data?.items ?? []}
mode="server"
pagination={{
page: tableState.pagination.pageIndex,
pageSize: tableState.pagination.pageSize,
totalRows: data?.totalRows ?? 0,
}}
onStateChange={setTableState}
isLoading={isLoading}
isFetching={isFetching}
/>
);
}
With Toolbar
<DataTable
columns={columns}
data={users}
toolbar={
<Button color="blue" onClick={() => setShowCreate(true)}>
Add User
</Button>
}
/>
With Row Click
import { useRouter } from 'next/navigation';
const router = useRouter();
<DataTable
columns={columns}
data={users}
onRowClick={(row) => router.push(`/users/${row.original.id}`)}
/>
Custom Empty State
import { EmptyState } from '@/components/ui';
import { UsersIcon } from '@/components/icons';
<DataTable
columns={columns}
data={users}
emptyMessage={
<EmptyState
icon={UsersIcon}
title="No users found"
description="Get started by adding your first user."
action={{
label: "Add User",
onClick: () => setShowCreate(true)
}}
/>
}
/>
Expandable Rows
<DataTable
columns={columns}
data={orders}
getRowCanExpand={() => true}
renderSubComponent={({ row }) => (
<div className="p-4 bg-zinc-50 dark:bg-zinc-800">
<h4 className="font-medium">Order Items</h4>
{row.original.items.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
)}
/>
Column Filtering
Enable filtering with column meta:
const columns: ColumnDef<User>[] = [
{
accessorKey: 'status',
header: 'Status',
meta: {
filterable: true,
filterType: 'select',
filterOptions: [
{ label: 'Active', value: 'active' },
{ label: 'Inactive', value: 'inactive' },
],
},
},
{
accessorKey: 'createdAt',
header: 'Created',
meta: {
filterable: true,
filterType: 'date',
},
},
];
Features
- Global search - Search across all columns
- Column filters - Filter individual columns
- Sorting - Click headers to sort
- Pagination - Client or server-side
- Loading states - Skeleton and fetching indicators
- Responsive - Horizontal scroll on small screens
- Dark mode - Full dark mode support
- Expandable rows - Show additional details
- Custom cells - Render custom content