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