Skip to content

Installation

How to install and set up the DataTable component in your project.

Before installing the DataTable component, make sure you have:

  1. A React project (Next.js, Vite, etc.)
  2. Shadcn UI set up in your project (Installation Guide)
  3. TailwindCSS configured
  4. TypeScript (recommended)

The DataTable components use a two-layer architecture for maximum flexibility:

  • Components (data-table-*.tsx in /components folder) - Context-aware wrapper components that use useDataTable() hook to automatically get the table from DataTableRoot context. These eliminate prop drilling and are the recommended way to use components:
    • DataTableSearchFilter, DataTableFilterMenu, DataTableSortMenu, DataTablePagination, etc.
  • Filters (table-*.tsx in /filters folder) - Core implementation components that accept a table prop directly and use TanStack Table hooks (like table.getState(), table.setGlobalFilter(), etc.). These can be used standalone:
    • TableSearchFilter, TableFilterMenu, TableSortMenu, etc.

Why this architecture?

  • Use DataTable* components from ”@/components/niko-table/components” when you want context-based, zero-config usage
  • Use Table* components from filters when you want to build custom components or manage the table instance yourself
  • All filter components use TanStack Table hooks directly, giving you full control

Learn more in the Introduction.

Important: Before installing DataTable, you must update your Shadcn table component. This is required for DataTable to work properly.

  1. Add the Shadcn table component (if you haven’t already)

    pnpm dlx npx shadcn@latest add table
  2. Update components/ui/table.tsx

    Replace the entire contents with this updated version:

    components/ui/table.tsx
    "use client"
    import * as React from "react"
    import { cn } from "@/lib/utils"
    function TableComponent({
    className,
    ...props
    }: React.ComponentProps<"table">) {
    return (
    <table
    data-slot="table"
    className={cn("w-full caption-bottom text-sm", className)}
    {...props}
    />
    )
    }
    function Table({ className, ...props }: React.ComponentProps<"table">) {
    return (
    <div
    data-slot="table-container"
    className="relative w-full overflow-x-auto"
    >
    <TableComponent className={className} {...props} />
    </div>
    )
    }
    function TableHeader({
    className,
    ...props
    }: React.ComponentProps<"thead">) {
    return (
    <thead
    data-slot="table-header"
    className={cn("[&_tr]:border-b", className)}
    {...props}
    />
    )
    }
    function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
    return (
    <tbody
    data-slot="table-body"
    className={cn("[&_tr:last-child]:border-0", className)}
    {...props}
    />
    )
    }
    function TableFooter({
    className,
    ...props
    }: React.ComponentProps<"tfoot">) {
    return (
    <tfoot
    data-slot="table-footer"
    className={cn(
    "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
    className,
    )}
    {...props}
    />
    )
    }
    function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
    return (
    <tr
    data-slot="table-row"
    className={cn(
    "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
    className,
    )}
    {...props}
    />
    )
    }
    function TableHead({ className, ...props }: React.ComponentProps<"th">) {
    return (
    <th
    data-slot="table-head"
    className={cn(
    "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]",
    className,
    )}
    {...props}
    />
    )
    }
    function TableCell({ className, ...props }: React.ComponentProps<"td">) {
    return (
    <td
    data-slot="table-cell"
    className={cn(
    "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]",
    className,
    )}
    {...props}
    />
    )
    }
    function TableCaption({
    className,
    ...props
    }: React.ComponentProps<"caption">) {
    return (
    <caption
    data-slot="table-caption"
    className={cn("mt-4 text-sm text-muted-foreground", className)}
    {...props}
    />
    )
    }
    export {
    TableComponent,
    Table,
    TableHeader,
    TableBody,
    TableFooter,
    TableHead,
    TableRow,
    TableCell,
    TableCaption,
    }

    What changed?

    • Added TableComponent export (used internally by DataTable)
    • Added data-slot attributes to all elements
    • Wrapped table in a container div for overflow handling

    Note: These changes are 100% backward compatible - your existing Shadcn tables will continue to work exactly as before.

Now you can install the core DataTable components:

pnpm dlx shadcn@latest add @niko-table/data-table

Want all components at once? Run this single command to install the core and every optional component:

pnpm dlx shadcn@latest add @niko-table/data-table @niko-table/data-table-virtualized @niko-table/data-table-pagination @niko-table/data-table-search-filter @niko-table/data-table-sort-menu @niko-table/data-table-view-menu @niko-table/data-table-clear-filter @niko-table/data-table-filter-menu @niko-table/data-table-faceted-filter @niko-table/data-table-inline-filter @niko-table/data-table-slider-filter @niko-table/data-table-date-filter @niko-table/data-table-export-button @niko-table/data-table-aside @niko-table/data-table-selection-bar @niko-table/data-table-column-sort @niko-table/data-table-column-hide @niko-table/data-table-column-pin @niko-table/data-table-column-faceted-filter @niko-table/data-table-column-slider-filter @niko-table/data-table-column-date-filter

Or install only the components you need. Each component can be added individually:

DataTablePagination:

pnpm dlx shadcn@latest add @niko-table/data-table-pagination

DataTableSearchFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-search-filter

DataTableSortMenu:

pnpm dlx shadcn@latest add @niko-table/data-table-sort-menu

DataTableViewMenu:

pnpm dlx shadcn@latest add @niko-table/data-table-view-menu

DataTableClearFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-clear-filter

DataTableExportButton:

pnpm dlx shadcn@latest add @niko-table/data-table-export-button

DataTableFilterMenu:

pnpm dlx shadcn@latest add @niko-table/data-table-filter-menu

DataTableFacetedFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-faceted-filter

DataTableInlineFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-inline-filter

DataTableSliderFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-slider-filter

DataTableDateFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-date-filter

DataTableColumnSort:

pnpm dlx shadcn@latest add @niko-table/data-table-column-sort

DataTableColumnHide:

pnpm dlx shadcn@latest add @niko-table/data-table-column-hide

DataTableColumnPin:

pnpm dlx shadcn@latest add @niko-table/data-table-column-pin

DataTableColumnFacetedFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-column-faceted-filter

DataTableColumnSliderFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-column-slider-filter

DataTableColumnDateFilter:

pnpm dlx shadcn@latest add @niko-table/data-table-column-date-filter

DataTableVirtualized:

pnpm dlx shadcn@latest add @niko-table/data-table-virtualized

DataTableAside:

pnpm dlx shadcn@latest add @niko-table/data-table-aside

DataTableSelectionBar:

pnpm dlx shadcn@latest add @niko-table/data-table-selection-bar

Prefer to copy and paste all components manually? Check out our Manual Installation Guide page where you can copy all the component files at once.

Let’s build your first table. We’ll start with a simple table and progressively add features.

  1. Start by defining your data

    The following data represents a list of users with their names and emails.

    components/example-table.tsx
    type User = {
    id: string
    name: string
    email: string
    status: "active" | "inactive"
    }
    const data: User[] = [
    { id: "1", name: "John Doe", email: "[email protected]", status: "active" },
    { id: "2", name: "Jane Smith", email: "[email protected]", status: "active" },
    { id: "3", name: "Bob Johnson", email: "[email protected]", status: "inactive" },
    ]
  2. Define your columns

    Columns define how your data is displayed in the table.

    components/example-table.tsx
    import type { DataTableColumnDef } from "@/components/niko-table/types"
    const columns: DataTableColumnDef<User>[] = [
    {
    accessorKey: "name",
    header: "Name",
    },
    {
    accessorKey: "email",
    header: "Email",
    },
    {
    accessorKey: "status",
    header: "Status",
    cell: ({ row }) => (
    <span
    className={
    row.original.status === "active"
    ? "text-green-600"
    : "text-gray-400"
    }
    >
    {row.original.status}
    </span>
    ),
    },
    ]
  3. Build your table

    You can now build your table using DataTable components.

    components/example-table.tsx
    import {
    DataTableRoot,
    DataTable,
    DataTableHeader,
    DataTableBody,
    } from "@/components/niko-table/core"
    export function ExampleTable() {
    return (
    <DataTableRoot data={data} columns={columns}>
    <DataTable>
    <DataTableHeader />
    <DataTableBody />
    </DataTable>
    </DataTableRoot>
    )
    }

Different examples require different dependencies. Here’s what you need for each:

  • @tanstack/react-table
  • Shadcn: table
  • @tanstack/react-table
  • Shadcn: table, button, dropdown-menu
  • @tanstack/react-table
  • Shadcn: table, button, input, dropdown-menu
  • @tanstack/react-table
  • Shadcn: table, button, input, dropdown-menu, popover, command, checkbox, select
  • @tanstack/react-table
  • Shadcn: table, button, checkbox, dropdown-menu
  • @tanstack/react-table
  • Shadcn: table, button, input, dropdown-menu
  • @tanstack/react-table
  • Shadcn: table, button, input, dropdown-menu
  • @tanstack/react-table
  • @tanstack/react-virtual
  • Shadcn: table, button, input, dropdown-menu, scroll-area
  • @tanstack/react-table
  • Shadcn: table, button, input, dropdown-menu, popover, command, checkbox, select, scroll-area, separator, skeleton, tooltip
  • Inline Filters: Included in DataTable components (no additional dependencies)
  • Sortable Rows (optional): DiceUI Sortable - @dnd-kit/core, @dnd-kit/modifiers, @dnd-kit/sortable, @dnd-kit/utilities, @radix-ui/react-slot
  • @tanstack/react-table
  • nuqs (nuqs.dev) - Type-safe search params state manager for URL state management
  • Shadcn: All components from Advanced Table
  • Inline Filters: Included in DataTable components (no additional dependencies)
  • Sortable Rows (optional): DiceUI Sortable - @dnd-kit/core, @dnd-kit/modifiers, @dnd-kit/sortable, @dnd-kit/utilities, @radix-ui/react-slot

Some advanced features may require additional dependencies:

For URL state persistence in tables, install nuqs:

npm install nuqs

nuqs provides type-safe search params state management, perfect for syncing table filters, pagination, and sorting with the URL.

For drag-and-drop row reordering, follow the DiceUI Sortable installation guide.

If you want to install everything upfront:

pnpm add npm install @tanstack/react-table @tanstack/react-virtual nuqs @dnd-kit/core @dnd-kit/modifiers @dnd-kit/sortable @dnd-kit/utilities @radix-ui/react-slot
pnpm dlx npx shadcn@latest add table button input dropdown-menu popover command checkbox select scroll-area separator skeleton tooltip
pnpm dlx npx shadcn@latest add "https://diceui.com/r/sortable"

Important: The default shadcn table component needs to be updated to work properly with DataTable. After installing the table component, update your components/ui/table.tsx file.

Note: These changes are backward compatible and won’t break any existing shadcn UI table usage. The updated component maintains all the same functionality and API, just adds the TableComponent export and data-slot attributes needed by DataTable.

components/ui/table.tsx
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
function TableComponent({
className,
...props
}: React.ComponentProps<"table">) {
return (
<table
data-slot="table"
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
)
}
function Table({ className, ...props }: React.ComponentProps<"table">) {
return (
<div
data-slot="table-container"
className="relative w-full overflow-x-auto"
>
<TableComponent className={className} {...props} />
</div>
)
}
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
return (
<thead
data-slot="table-header"
className={cn("[&_tr]:border-b", className)}
{...props}
/>
)
}
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
return (
<tbody
data-slot="table-body"
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
)
}
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
return (
<tfoot
data-slot="table-footer"
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className,
)}
{...props}
/>
)
}
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
return (
<tr
data-slot="table-row"
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className,
)}
{...props}
/>
)
}
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
return (
<th
data-slot="table-head"
className={cn(
"h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
/>
)
}
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
return (
<td
data-slot="table-cell"
className={cn(
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
/>
)
}
function TableCaption({
className,
...props
}: React.ComponentProps<"caption">) {
return (
<caption
data-slot="table-caption"
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
)
}
export {
TableComponent,
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

The key changes are:

  • Added TableComponent export (used internally by DataTable)
  • Added data-slot attributes to all table elements
  • Updated Table to wrap TableComponent in a container div

After installation, your project structure should look like:

src/
├── components/
│ ├── ui/ # Shadcn UI components
│ └── niko-table/ # DataTable components
│ ├── core/
│ ├── types/
│ ├── hooks/
│ ├── components/
│ ├── filters/
│ ├── config/
│ ├── lib/
│ └── index.tsx
└── lib/
└── utils.ts

Create a simple test to verify everything is working:

import {
DataTableRoot,
DataTable,
DataTableHeader,
DataTableBody,
} from "@/components/niko-table/core"
const columns = [
{ accessorKey: "name", header: "Name" },
{ accessorKey: "email", header: "Email" },
]
const data = [
{ id: "1", name: "John Doe", email: "[email protected]" },
{ id: "2", name: "Jane Smith", email: "[email protected]" },
]
export function TestTable() {
return (
<DataTableRoot data={data} columns={columns}>
<DataTable>
<DataTableHeader />
<DataTableBody />
</DataTable>
</DataTableRoot>
)
}

Now that you have the DataTable installed, check out the examples: