TNKS Data Table

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.json

Set Up React Query Provider

Wrap your app with React Query provider in your root layout:

src/app/layout.tsx
"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:

src/app/(section)/users/schema/index.ts
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:

src/api/user/fetch-users.ts
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:

src/app/(section)/users/utils/data-fetching.ts
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:

src/app/(section)/users/components/columns.tsx
"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:

src/app/(section)/users/index.tsx
"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:

src/app/(section)/users/page.tsx
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:

Need Help?

How is this guide?