Skip to main content
Version: 5.xx.xx
Source Code

<DataTable />

When you're building admin dashboards, you'll frequently need to display lists of data - whether it's posts, users, products, or any other resource. The DataTable component handles all the common table functionality you need: sorting columns, filtering data, pagination, and action buttons.

Built on TanStack Table, it integrates directly with Refine's data fetching hooks, so you get features like server-side sorting and filtering without extra configuration.

Installation

Add the DataTable components to your project:

npx shadcn@latest add https://ui.refine.dev/r/data-table.json

This installs the main DataTable component plus all the filtering, sorting, and pagination components you'll need. The CLI will automatically install the required dependencies like @tanstack/react-table and react-day-picker.

Basic Usage

Create a list view with a data table:

import { useMemo } from "react";
import { useTable } from "@refinedev/react-table";
import type { ColumnDef } from "@tanstack/react-table";
import { DataTable } from "@/components/refine-ui/data-table/data-table";
import {
ListView,
ListViewHeader,
} from "@/components/refine-ui/views/list-view";

type Post = {
id: number;
title: string;
status: string;
};

export default function PostList() {
const columns = useMemo<ColumnDef<Post>[]>(
() => [
{
// Column for ID field
id: "id",
accessorKey: "id", // Maps to the 'id' field in your data
header: "ID",
},
{
// Column for title field
id: "title",
accessorKey: "title", // Maps to the 'title' field in your data
header: "Title",
},
{
// Column for status field
id: "status",
accessorKey: "status", // Maps to the 'status' field in your data
header: "Status",
},
],
[],
);

const table = useTable<Post>({
columns,
refineCoreProps: {
resource: "posts", // Your API resource name
},
});

return (
<ListView>
<ListViewHeader title="Posts" />
<DataTable table={table} />
</ListView>
);
}

Advanced Example

Here's a comprehensive example showing how to create a data table with filtering, sorting, and pagination:

import { useMemo } from "react";
import { useTable } from "@refinedev/react-table";
import type { ColumnDef } from "@tanstack/react-table";
import { useList } from "@refinedev/core";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { MoreHorizontal } from "lucide-react";
import { DataTable } from "@/components/refine-ui/data-table/data-table";
import { DataTableSorter } from "@/components/refine-ui/data-table/data-table-sorter";
import {
DataTableFilterDropdownText,
DataTableFilterCombobox,
DataTableFilterDropdownNumeric,
} from "@/components/refine-ui/data-table/data-table-filter";
import {
EditButton,
DeleteButton,
ShowButton,
} from "@/components/refine-ui/buttons";
import {
ListView,
ListViewHeader,
} from "@/components/refine-ui/views/list-view";

type Post = {
id: number;
title: string;
content: string;
status: "published" | "draft" | "rejected";
category: { id: number };
createdAt: string;
};

export default function PostList() {
// Fetch categories for the dropdown filter
const { data: categories } = useList({
resource: "categories",
});

const columns = useMemo<ColumnDef<Post>[]>(
() => [
{
// ID column with sorting capability
id: "id",
accessorKey: "id",
size: 80,
header: ({ column }) => (
<DataTableSorter column={column}>ID</DataTableSorter>
),
cell: ({ getValue }) => getValue(),
},
{
// Title column with sorting and text filtering
id: "title",
accessorKey: "title",
size: 300,
header: ({ column }) => (
<div className="flex items-center gap-1">
<DataTableSorter column={column}>Title</DataTableSorter>
<DataTableFilterDropdownText column={column} />
</div>
),
},
{
// Status column with dropdown filter for specific values
id: "status",
accessorKey: "status",
size: 120,
header: ({ column }) => (
<div className="flex items-center gap-1">
<span>Status</span>
<DataTableFilterCombobox
column={column}
options={[
{ label: "Published", value: "published" },
{ label: "Draft", value: "draft" },
{ label: "Rejected", value: "rejected" },
]}
/>
</div>
),
},
{
// Actions column with edit/delete/show buttons
id: "actions",
size: 100,
enableSorting: false, // Disable sorting for action buttons
enableColumnFilter: false, // Disable filtering for action buttons
header: "Actions",
cell: ({ row }) => (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<ShowButton hideText size="sm" recordItemId={row.original.id} />
<EditButton hideText size="sm" recordItemId={row.original.id} />
<DeleteButton hideText size="sm" recordItemId={row.original.id} />
</DropdownMenuContent>
</DropdownMenu>
),
},
],
[categories], // Re-create columns when categories data changes
);

const table = useTable<Post>({
columns,
refineCoreProps: {
resource: "posts",
},
});

return (
<ListView>
<ListViewHeader title="Posts" />
<DataTable table={table} />
</ListView>
);
}

This example demonstrates the key features: sortable columns, multiple filter types (text, numeric, dropdown), and action buttons in a dropdown menu.

Key Features

Column Sorting

Click any column header to sort by that field. The DataTable automatically handles the sorting state and sends the appropriate parameters to your data provider.

Filtering Options

The DataTable supports several filter types:

  • Text filters: Search within text columns
  • Numeric filters: Range filtering for numbers
  • Dropdown filters: Select from predefined options
  • Date filters: Date range selection

Action Buttons

Add action buttons for each row using the dropdown menu pattern shown above.

Component Structure

The DataTable is built from several focused components:

  • DataTable: Main table component that handles rendering
  • DataTableSorter: Column header sorting controls
  • DataTableFilter: Various filter components for different data types
  • DataTablePagination: Pagination controls

Handling Relational Data

When your main data records (e.g., posts) contain references to other resources (e.g., a category with an id), you often need to fetch and display details from these related resources.

The @refinedev/core package provides the useMany hook, which is ideal for this scenario. You can collect all the foreign key IDs (e.g., category.id from all posts in the current table view) and pass them to useMany to fetch all related categories in a single request.

The recommended way to make this related data available to your column cell renderers is by using the setOptions function returned by useTable (from @refinedev/react-table) to add the fetched data to the table's meta object.

Adding Filters and Sorting

You can add different types of filters to your columns. Here are the most common ones:

Text Search Filter

import { DataTableFilterDropdownText } from "@/components/refine-ui/data-table/data-table-filter";

// In your column definition:
header: ({ column }) => (
<div className="flex items-center gap-1">
<DataTableSorter column={column}>Title</DataTableSorter>
<DataTableFilterDropdownText column={column} />
</div>
),
import { DataTableFilterCombobox } from "@/components/refine-ui/data-table/data-table-filter";

// In your column definition:
header: ({ column }) => (
<div className="flex items-center gap-1">
<span>Status</span>
<DataTableFilterCombobox
column={column}
options={[
{ label: "Published", value: "published" },
{ label: "Draft", value: "draft" },
]}
/>
</div>
),

Numeric Range Filter

import { DataTableFilterDropdownNumeric } from "@/components/refine-ui/data-table/data-table-filter";

// In your column definition:
header: ({ column }) => (
<div className="flex items-center gap-1">
<DataTableSorter column={column}>Price</DataTableSorter>
<DataTableFilterDropdownNumeric column={column} />
</div>
),

Date Range Filter

import { DataTableFilterDropdownDateRangePicker } from "@/components/refine-ui/data-table/data-table-filter";

// In your column definition:
header: ({ column }) => (
<div className="flex items-center gap-1">
<span>Created</span>
<DataTableFilterDropdownDateRangePicker
column={column}
formatDateRange={(dateRange) => {
if (!dateRange?.from || !dateRange?.to) return undefined;
return [
dateRange.from.toISOString(),
dateRange.to.toISOString(),
];
}}
/>
</div>
),

Column Sorting

import { DataTableSorter } from "@/components/refine-ui/data-table/data-table-sorter";

// In your column definition:
header: ({ column }) => (
<DataTableSorter column={column}>Column Name</DataTableSorter>
),

Props

DataTable

PropTypeDescription
tableTable<TData>TanStack Table instance from useTable hook

DataTableSorter

PropTypeDescription
columnColumn<TData>TanStack Table column instance
childrenReact.ReactNodeContent to display (usually column name)

DataTableFilterDropdownText

PropTypeDescription
columnColumn<TData>TanStack Table column instance

DataTableFilterCombobox

PropTypeDescription
columnColumn<TData>TanStack Table column instance
optionsArray<{ label: string; value: string }>Dropdown options
multiplebooleanAllow multiple selections

DataTableFilterDropdownNumeric

PropTypeDescription
columnColumn<TData>TanStack Table column instance