Quick Start
Get up and running with the Advanced Data Table Component in minutes
Quick Start Guide
This guide will help you implement your first data table in just a few steps.
Prerequisites
Before you begin, make sure you have:
- Next.js 15+ with App Router
- React 19+
- TypeScript 5+
- Tailwind CSS configured
- Shadcn UI components installed
Installation
The easiest way to install the data table is using the Shadcn CLI with our custom registry.
# Install required Shadcn UI components first
npx shadcn@latest init
npx shadcn@latest add button checkbox input select popover calendar dropdown-menu separator table command
# Install the data-table component
npx shadcn@latest add https://tnks-data-table.vercel.app/r/data-table.json
# Install the calendar-date-picker (required dependency)
npx shadcn@latest add https://tnks-data-table.vercel.app/r/calendar-date-picker.json# Clone the repository
git clone https://github.com/jacksonkasi1/tnks-data-table.git
cd tnks-data-table
# Install dependencies and build registry
pnpm install
pnpm run registry:build
# Start the development server
pnpm run dev
# In your project directory, install from localhost
npx shadcn@latest add http://localhost:3000/r/data-table.json
npx shadcn@latest add http://localhost:3000/r/calendar-date-picker.jsonSet Up React Query Provider
Wrap your app with React Query provider in your root layout:
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
import { Toaster } from "@/components/ui/sonner";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const [queryClient] = useState(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 minute
retry: false,
},
},
}));
return (
<html lang="en">
<body>
<QueryClientProvider client={queryClient}>
{children}
<Toaster />
</QueryClientProvider>
</body>
</html>
);
}Create Your First Data Table
Follow these steps to create a basic data table:
Define Your Data Schema
Create a Zod schema for type validation and TypeScript types:
import { z } from "zod";
export const userSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string(),
age: z.number(),
created_at: z.string(),
});
export type User = z.infer<typeof userSchema>;
export const usersResponseSchema = z.object({
success: z.boolean(),
data: z.array(userSchema),
pagination: z.object({
page: z.number(),
limit: z.number(),
total_pages: z.number(),
total_items: z.number(),
}),
});Create API Functions
Implement the API function to fetch users:
import { usersResponseSchema } from "@/app/(section)/users/schema";
const API_BASE_URL = "/api";
export async function fetchUsers({
search = "",
from_date = "",
to_date = "",
sort_by = "created_at",
sort_order = "desc",
page = 1,
limit = 10,
}) {
const params = new URLSearchParams();
if (search) params.append("search", search);
if (from_date) params.append("from_date", from_date);
if (to_date) params.append("to_date", to_date);
params.append("sort_by", sort_by);
params.append("sort_order", sort_order);
params.append("page", page.toString());
params.append("limit", limit.toString());
const response = await fetch(`${API_BASE_URL}/users?${params.toString()}`);
if (!response.ok) {
throw new Error(`Failed to fetch users: ${response.statusText}`);
}
const data = await response.json();
return usersResponseSchema.parse(data);
}Create Data Fetching Hook
Create a custom hook using React Query:
import { useQuery, keepPreviousData } from "@tanstack/react-query";
import { fetchUsers } from "@/api/user/fetch-users";
export function useUsersData(
page: number,
pageSize: number,
search: string,
dateRange: { from_date: string; to_date: string },
sortBy: string,
sortOrder: string
) {
return useQuery({
queryKey: ["users", page, pageSize, search, dateRange, sortBy, sortOrder],
queryFn: () =>
fetchUsers({
page,
limit: pageSize,
search,
from_date: dateRange.from_date,
to_date: dateRange.to_date,
sort_by: sortBy,
sort_order: sortOrder,
}),
placeholderData: keepPreviousData,
});
}
// Required for DataTable component
useUsersData.isQueryHook = true;Define Table Columns
Create column definitions with headers and cell renderers:
"use client";
import { format } from "date-fns";
import { ColumnDef } from "@tanstack/react-table";
import { DataTableColumnHeader } from "@/components/data-table/column-header";
import { Checkbox } from "@/components/ui/checkbox";
import { User } from "../schema";
export const getColumns = (
handleRowDeselection?: ((rowId: string) => void) | null
): ColumnDef<User>[] => {
const baseColumns: ColumnDef<User>[] = [
{
accessorKey: "name",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Name" />
),
cell: ({ row }) => (
<div className="font-medium">{row.getValue("name")}</div>
),
size: 200,
},
{
accessorKey: "email",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Email" />
),
size: 250,
},
{
accessorKey: "age",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Age" />
),
size: 100,
},
{
accessorKey: "created_at",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Created" />
),
cell: ({ row }) => {
const date = new Date(row.getValue("created_at"));
return <div>{format(date, "MMM d, yyyy")}</div>;
},
size: 120,
},
];
// Add selection column if row selection is enabled
if (handleRowDeselection !== null) {
return [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={table.getIsAllPageRowsSelected()}
onCheckedChange={(value) =>
table.toggleAllPageRowsSelected(!!value)
}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
size: 50,
},
...baseColumns,
];
}
return baseColumns;
};Implement the Data Table
Create the main table component:
"use client";
import { DataTable } from "@/components/data-table/data-table";
import { getColumns } from "./components/columns";
import { useUsersData } from "./utils/data-fetching";
import { User } from "./schema";
export default function UsersTable() {
return (
<DataTable<User, any>
getColumns={getColumns}
fetchDataFn={useUsersData}
idField="id"
pageSizeOptions={[10, 20, 50, 100]}
config={{
enableRowSelection: true,
enableSearch: true,
enableDateFilter: true,
enableColumnVisibility: true,
enableUrlState: true,
}}
/>
);
}Add to Your Page
Finally, add the table to your page component:
import { Metadata } from "next";
import { Suspense } from "react";
import UsersTable from "./users-table";
export const metadata: Metadata = {
title: "Users Management",
};
export default function UsersPage() {
return (
<main className="container mx-auto py-10">
<h1 className="text-2xl font-bold mb-6">Users</h1>
<Suspense fallback={<div>Loading...</div>}>
<UsersTable />
</Suspense>
</main>
);
}What You've Built
Congratulations! You now have a fully functional data table with:
- ✅ Server-side pagination, sorting, and filtering
- ✅ Row selection with checkboxes
- ✅ Search functionality
- ✅ Date range filtering
- ✅ Column visibility controls
- ✅ URL state persistence
- ✅ Responsive design
Next Steps
Now that you have a basic table running, explore more features:
- Add row actions for edit and delete operations
- Configure export to CSV and Excel
- Implement subrows for hierarchical data
- Customize the toolbar with your own actions
- Learn about URL state management
Need Help?
- Check the Examples section for more implementations
- Review the API Reference for all available options
- Read the Troubleshooting Guide for common issues
How is this guide?