Data Table
A powerful and flexible data table component for displaying, filtering, sorting, and paginating tabular data.
"use client";
import type { Column, ColumnDef } from "@tanstack/react-table";
import {
CheckCircle,
CheckCircle2,
DollarSign,
MoreHorizontal,
Text,
XCircle,
} from "lucide-react";
import { parseAsArrayOf, parseAsString, useQueryState } from "nuqs";
import * as React from "react";
import { DataTable } from "@/components/data-table";
import { DataTableColumnHeader } from "@/components/data-table-column-header";
import { DataTableToolbar } from "@/components/data-table-toolbar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useDataTable } from "@/hooks/use-data-table";
interface Project {
id: string;
title: string;
status: "active" | "inactive";
budget: number;
}
const data: Project[] = [
{
id: "1",
title: "Project Alpha",
status: "active",
budget: 50000,
},
{
id: "2",
title: "Project Beta",
status: "inactive",
budget: 75000,
},
{
id: "3",
title: "Project Gamma",
status: "active",
budget: 25000,
},
{
id: "4",
title: "Project Delta",
status: "active",
budget: 100000,
},
];
export function DataTableDemo() {
const [title] = useQueryState("title", parseAsString.withDefault(""));
const [status] = useQueryState(
"status",
parseAsArrayOf(parseAsString).withDefault([]),
);
// Ideally we would filter the data server-side, but for the sake of this example, we'll filter the data client-side
const filteredData = React.useMemo(() => {
return data.filter((project) => {
const matchesTitle =
title === "" ||
project.title.toLowerCase().includes(title.toLowerCase());
const matchesStatus =
status.length === 0 || status.includes(project.status);
return matchesTitle && matchesStatus;
});
}, [title, status]);
const columns = React.useMemo<ColumnDef<Project>[]>(
() => [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) =>
table.toggleAllPageRowsSelected(!!value)
}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
size: 32,
enableSorting: false,
enableHiding: false,
},
{
id: "title",
accessorKey: "title",
header: ({ column }: { column: Column<Project, unknown> }) => (
<DataTableColumnHeader column={column} title="Title" />
),
cell: ({ cell }) => <div>{cell.getValue<Project["title"]>()}</div>,
meta: {
label: "Title",
placeholder: "Search titles...",
variant: "text",
icon: Text,
},
enableColumnFilter: true,
},
{
id: "status",
accessorKey: "status",
header: ({ column }: { column: Column<Project, unknown> }) => (
<DataTableColumnHeader column={column} title="Status" />
),
cell: ({ cell }) => {
const status = cell.getValue<Project["status"]>();
const Icon = status === "active" ? CheckCircle2 : XCircle;
return (
<Badge variant="outline" className="capitalize">
<Icon />
{status}
</Badge>
);
},
meta: {
label: "Status",
variant: "multiSelect",
options: [
{ label: "Active", value: "active", icon: CheckCircle },
{ label: "Inactive", value: "inactive", icon: XCircle },
],
},
enableColumnFilter: true,
},
{
id: "budget",
accessorKey: "budget",
header: ({ column }: { column: Column<Project, unknown> }) => (
<DataTableColumnHeader column={column} title="Budget" />
),
cell: ({ cell }) => {
const budget = cell.getValue<Project["budget"]>();
return (
<div className="flex items-center gap-1">
<DollarSign className="size-4" />
{budget.toLocaleString()}
</div>
);
},
},
{
id: "actions",
cell: function Cell() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Open menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem variant="destructive">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
size: 32,
},
],
[],
);
const { table } = useDataTable({
data: filteredData,
columns,
pageCount: 1,
initialState: {
sorting: [{ id: "title", desc: true }],
columnPinning: { right: ["actions"] },
},
getRowId: (row) => row.id,
});
return (
<div className="data-table-container">
<DataTable table={table}>
<DataTableToolbar table={table} />
</DataTable>
</div>
);
}
Installation
Install the components and dependencies:
npx [email protected] add "https://diceui.com/r/data-table"
Wrap your application with the NuqsAdapter
for query state management:
import { NuqsAdapter } from "nuqs/adapters/next/app";
<NuqsAdapter>
<App />
</NuqsAdapter>
Layout
Import the components and compose them together:
import { DataTable } from "@/components/data-table";
import { DataTableToolbar } from "@/components/data-table-toolbar";
import { DataTableAdvancedToolbar } from "@/components/data-table-advanced-toolbar";
import { DataTableFilterList } from "@/components/data-table-filter-list";
import { DataTableSortList } from "@/components/data-table-sort-list";
import { useDataTable } from "@/hooks/use-data-table";
const { table } = useDataTable({
data,
columns,
pageCount,
});
// With standard toolbar
<DataTable table={table}>
<DataTableToolbar table={table}>
<DataTableSortList table={table} />
</DataTableToolbar>
</DataTable>
// With advanced toolbar
<DataTable table={table}>
<DataTableAdvancedToolbar table={table}>
<DataTableFilterList table={table} />
<DataTableSortList table={table} />
</DataTableAdvancedToolbar>
</DataTable>
Sort List
The DataTableSortList
provides a comprehensive way to sort data by multiple columns simultaneously.
Features
- Supports multiple column sorting
- Drag and drop reordering
- Ascending and descending directions
Installation
npx [email protected] add "https://diceui.com/r/data-table-sort-list"
Filter List
The DataTableFilterList
provides a comprehensive way to filter data with multiple conditions.
Features
- Multiple filter conditions with AND/OR logic
- Drag and drop reordering
- Dynamic operators per field type
Installation
npx [email protected] add "https://diceui.com/r/data-table-filter-list"
Filter Menu
The DataTableFilterMenu
provides a command palette-style interface for quickly adding and managing filters.
Features
- Command palette-style interface
- Context-aware input fields
- Compact token display
Installation
npx [email protected] add "https://diceui.com/r/data-table-filter-menu"
Action Bar
The DataTableActionBar
provides a toolbar for the data table when rows are selected.
Features
- Floating action bar
- Customizable actions
- Row selection tracking
Installation
npx [email protected] add "https://diceui.com/r/data-table-action-bar"
Walkthrough
Define columns with appropriate metadata:
import { Text, CalendarIcon, DollarSign } from "lucide-react";
import { DataTableColumnHeader } from "@/components/data-table-column-header";
const columns = React.useMemo(() => [
{
// Provide an unique id for the column
// This id will be used as query key for the column filter
id: "title",
accessorKey: "title",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Title" />
),
cell: ({ row }) => <div>{row.getValue("title")}</div>,
// Define the column meta options for sorting, filtering, and view options
meta: {
label: "Title",
placeholder: "Search titles...",
variant: "text",
icon: Text,
},
// By default, the column will not be filtered. Set to `true` to enable filtering.
enableColumnFilter: true,
},
], []);
Initialize the table state using the useDataTable
hook:
import { useDataTable } from "@/hooks/use-data-table";
function DataTableDemo() {
const { table } = useDataTable({
data,
columns,
// Pass the total number of pages for the table
pageCount,
initialState: {
sorting: [{ id: "createdAt", desc: true }],
pagination: { pageSize: 10 },
},
// Unique identifier for rows, can be used for unique row selection
getRowId: (row) => row.id,
});
return (
// ... render table
);
}
Pass the table instance to the DataTable
, and DataTableToolbar
components:
import { DataTable } from "@/components/data-table";
import { DataTableToolbar } from "@/components/data-table-toolbar";
import { DataTableSortList } from "@/components/data-table-sort-list";
function DataTableDemo() {
return (
<DataTable table={table}>
<DataTableToolbar table={table}>
<DataTableSortList table={table} />
</DataTableToolbar>
</DataTable>
);
}
For advanced filtering, use the DataTableAdvancedToolbar
component:
import { DataTableAdvancedToolbar } from "@/components/data-table-advanced-toolbar";
import { DataTableFilterList } from "@/components/data-table-filter-list";
import { DataTableFilterMenu } from "@/components/data-table-filter-menu";
function DataTableDemo() {
return (
<DataTable table={table}>
<DataTableAdvancedToolbar table={table}>
<DataTableFilterList table={table} />
<DataTableSortList table={table} />
</DataTableAdvancedToolbar>
</DataTable>
);
}
Alternatively, swap out DataTableFilterList
with DataTableFilterMenu
for a command palette-style interface:
import { DataTableAdvancedToolbar } from "@/components/data-table-advanced-toolbar";
import { DataTableFilterList } from "@/components/data-table-filter-list";
import { DataTableFilterMenu } from "@/components/data-table-filter-menu";
import { DataTableSortList } from "@/components/data-table-sort-list";
function DataTableDemo() {
return (
<DataTable table={table}>
<DataTableAdvancedToolbar table={table}>
<DataTableFilterList table={table} />
<DataTableFilterMenu table={table} />
<DataTableSortList table={table} />
</DataTableAdvancedToolbar>
</DataTable>
);
}
Render an action bar on row selection:
import { DataTableActionBar } from "@/components/data-table-action-bar";
import { CustomTableActions } from "@/components/custom-table-actions";
function DataTableDemo() {
return (
<DataTable
table={table}
actionBar={
<DataTableActionBar table={table}>
{/* Add your custom actions here */}
<CustomTableActions />
</DataTableActionBar>
}
>
<DataTableToolbar table={table} />
</DataTable>
);
}
API Reference
Column Definitions
The column definitions are used to define the columns of the data table.
const columns = React.useMemo<ColumnDef<Project>[]>(() => [
{
// Required: Unique identifier for the column
id: "title",
// Required: Key to access the data, `accessorFn` can also be used
accessorKey: "title",
// Optional: Custom header component
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Title" />
),
// Optional: Custom cell component
cell: ({ row }) => <div>{row.getValue("title")}</div>,
// Optional: Meta options for filtering, sorting, and view options
meta: {
label: "Title",
placeholder: "Search titles...",
variant: "text",
icon: Text,
},
// By default, the column will not be filtered. Set to `true` to enable filtering.
enableColumnFilter: true,
},
{
id: "status",
// Access nested data using `accessorFn`
accessorFn: (row) => row.lineItem.status,
header: "Status",
meta: {
label: "Status",
variant: "select",
options: [
{ label: "Active", value: "active" },
{ label: "Inactive", value: "inactive" },
],
},
enableColumnFilter: true,
},
], []);
Properties
Core configuration options for defining columns.
Prop | Description |
---|---|
id | Required: Unique identifier for the column |
accessorKey | Required: Key to access the data from the row |
accessorFn | Optional: Custom accessor function to access data |
header | Optional: Custom header component with column props |
cell | Optional: Custom cell component with row props |
meta | Optional: Meta options for accessing column metadata |
enableColumnFilter | By default, the column will not be filtered. Set to `true` to enable filtering |
enableSorting | Enable sorting for this column |
enableHiding | Enable column visibility toggle |
Column Meta
Column meta options for filtering, sorting, and view options.
Prop | Description |
---|---|
label | The display name for the column |
placeholder | The placeholder text for filter inputs |
variant | The type of filter to use (`text`, `number`, `select`, etc.) |
options | For select/multi-select filters, an array of options with `label`, `value`, and optional `count` and `icon` |
range | For range filters, a tuple of `[min, max]` values |
unit | For numeric filters, the unit to display (e.g., 'hr', '$') |
icon | The react component to use as an icon for the column |
Filter Variants
Available filter variants for column meta.
Title | Description |
---|---|
text | Text search with contains, equals, etc. |
number | Numeric filters with equals, greater than, less than, etc. |
range | Range filters with minimum and maximum values |
date | Date filters with equals, before, after, etc. |
dateRange | Date range filters with start and end dates |
boolean | Boolean filters with true/false values |
select | Single-select filters with predefined options |
multiSelect | Multi-select filters with predefined options |
Reference the TanStack Table Column Definitions Guide for detailed column definition guide.
useDataTable
A hook for initializing the data table with state management.
Prop
Type
DataTable
The main data table component.
Prop
Type
DataTableColumnHeader
Custom header component for columns with sorting.
Prop
Type
DataTableToolbar
Standard toolbar with filtering and view options.
Prop
Type
DataTableAdvancedToolbar
Advanced toolbar with more comprehensive filtering capabilities.
Prop
Type
DataTableViewOptions
Controls column visibility and display preferences in the data table.
Prop
Type
DataTableSortList
List of applied sorting with ability to add, remove, and modify sorting.
Prop
Type
DataTableFilterList
List of applied filters with ability to add, remove, and modify filters.
Prop
Type
DataTableFilterMenu
Filter menu with ability to add, remove, and modify filters.
Prop
Type
DataTableActionBar
Floating action bar component for actions for selected rows.
Prop
Type
DataTablePagination
Pagination controls for the data table.
Prop
Type
Accessibility
Keyboard Interactions
Key | Description |
---|---|
F | Opens the filter menu. |
Shift + F | Removes the last applied filter. |
S | Opens the sort menu. |
Shift + S | Removes the last applied sort. |
BackspaceDelete | Removes the focused filter/sort item. Removes the last applied filter/sort when menu trigger is focused. |
Credits
- shadcn/ui - For the initial implementation of the data table.