Skip to content

Introduction

Open-code React data tables for shadcn/ui—composable DataTable* components, TanStack Table state, and copy-paste registry installs.

Nobody’s table, everyone’s solution.

Just like Shadcn, Niko Table is not a component library. Instead, it’s a comprehensive DataTable component that you copy into your project and customize to your needs. It’s built with TanStack Table and Shadcn UI, providing enterprise-grade features while remaining fully under your control.

Built for real-world applications with features like:

  • Sorting (single and multi-column)
  • Filtering (global search and column filters)
  • Pagination (client and server-side)
  • Row selection and bulk actions
  • Column visibility and reordering
  • Virtual scrolling for large datasets
  • Export to CSV/Excel

Start simple and add features as you need them:

  1. Simple Table - Just render data (< 5 minutes)
  2. Basic Table - Add pagination and sorting (+ 5 minutes)
  3. Search Table - Add global search (+ 5 minutes)
  4. Filter Table - Add column filters (+ 10 minutes)
  5. Advanced Table - Add virtualization, sidebars, etc. (+ 15 minutes)

Since you own the code, you can:

  • Modify any component
  • Add custom features
  • Change styling
  • Integrate with your backend
  • No version lock-in

Built on Radix UI primitives with semantic table markup where possible:

  • Keyboard support in sort/filter menus and toolbar controls
  • ARIA labels on filter triggers, sliders, and clear actions
  • Screen-reader-friendly patterns via Shadcn/Radix components
  • jsx-a11y lint rules on docs and source

We do not claim third-party WCAG certification; treat complex tables (virtualization, DnD, pinning) like any custom UI and verify with your own accessibility checklist.

  • DataTable uses a full-width overflow-auto scroll region for wide column sets on small screens
  • Popovers and filter panels respect available viewport width
  • Optional DataTableAside for a collapsible detail panel (see Aside Table)
  • Toolbars are plain flex layouts — wrap or hide filters on mobile in your app as needed

The DataTable uses 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 their direct paths (e.g. @/components/niko-table/components/data-table-pagination) 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
// Recommended: Use context-based Action components
<DataTableRoot data={data} columns={columns}>
<DataTableSearchFilter /> {/* Automatically gets table from context */}
<DataTable>
<DataTableHeader />
<DataTableBody />
</DataTable>
<DataTablePagination />
</DataTableRoot>
// Advanced: Use Filter components directly
const table = useReactTable({ data, columns, ... })
<TableSearchFilter table={table} /> {/* Pass table prop directly */}

Build tables by composing components - start simple and add features as needed:

<DataTableRoot data={data} columns={columns}>
<DataTableToolbarSection>
<DataTableSearchFilter />
<DataTableFilterMenu />
</DataTableToolbarSection>
<DataTable>
<DataTableHeader />
<DataTableBody />
</DataTable>
<DataTablePagination />
</DataTableRoot>

Niko Table is mix-and-match: install only the registry items you need, render only the children you want, and swap body strategies without rewriting column defs.

| Layer | What you choose | Notes | | —————–– | —————————————————————————————–– | ———————————————————————————————–– | ———————— | | Root | DataTableRoot | Required. Optional controlled state, config overrides, getRowId for selection/DnD/expansion | | Toolbar | DataTableToolbarSection + any of search, faceted, filter menu, view menu, clear | Omitted entirely for minimal tables | | Table shell | DataTable with height / max-h-* when virtualizing | Virtualizer needs a bounded scroll container | | Header / body | Regular: DataTableHeader + DataTableBody | Default path | | | Virtualized: DataTableVirtualizedHeader + DataTableVirtualizedBody | 10k+ rows | | | Row DnD: DataTableRowDndProvider (outside table) + DataTableDndBody | Do not combine with sort/filter | | | Column DnD | DataTableColumnDndProvider + draggable headers/cells | Safe with other features | | Loading / empty | DataTableSkeleton, DataTableEmptyBody or composable empty pieces | Siblings inside the body | | Footer | DataTablePagination, DataTableSelectionBar | Optional | | Row actions | One RowMenu* component + useDataTableRow in kebab and DataTableRowContextMenuSlot | Row context menu | | Escape hatch | Table* filters with your own table instance | Custom layouts without context |

DataTableRoot runs feature detection on children (detectFeaturesFromChildren) so pagination, filtering, and sorting turn on when you add the matching components — you rarely hand-roll a giant config object.

This DataTable provides you with:

  • TanStack Table integration - Built on the powerful headless table library
  • Composable components - Build complex UIs from simple, reusable pieces
  • State management - Context-based state sharing with hook-based flexibility
  • Performance optimization - Virtual scrolling, memoization, and efficient rendering
  • Accessibility - Radix primitives, keyboard-friendly filters/menus, jsx-a11y linting (verify end-to-end in your app)
  • TypeScript - Fully typed with comprehensive type definitions

This project follows the Shadcn philosophy:

You copy the DataTable component code into your project (not install it as a package). However, you still need to install the required dependencies:

Required Dependencies:

  • @tanstack/react-table - Core table library
  • Shadcn UI components (via npx shadcn@latest add ...) - UI primitives
  • Optional: @tanstack/react-virtual, nuqs, @dnd-kit/* for advanced features

What You Copy:

  • All DataTable component files from /components/niko-table/
  • Full source code that you can read, modify, and customize

Benefits:

  • ✅ No black box - See exactly how everything works
  • ✅ Full control - Modify any component to fit your needs
  • ✅ Easy to customize - Change styling, behavior, or add features
  • ✅ No version lock-in - You control when to update
  • ✅ Learn by reading - Understand the implementation

Build complex tables by composing simple components:

// Start simple
<DataTableRoot data={data} columns={columns}>
<DataTable>
<DataTableHeader />
<DataTableBody />
</DataTable>
</DataTableRoot>
// Add features incrementally
<DataTableRoot data={data} columns={columns} config={{ enablePagination: true }}>
<DataTableToolbarSection>
<DataTableSearchFilter />
</DataTableToolbarSection>
<DataTable>
<DataTableHeader />
<DataTableBody />
</DataTable>
<DataTablePagination />
</DataTableRoot>
// Go advanced
<DataTableRoot
data={data}
columns={columns}
config={{
enablePagination: true,
enableFilters: true,
enableRowSelection: true,
}}
>
<DataTableToolbarSection className="justify-between">
<div className="flex gap-2">
<DataTableSearchFilter />
<DataTableFilterMenu />
</div>
<div className="flex gap-2">
<DataTableViewMenu />
<DataTableSortMenu />
</div>
</DataTableToolbarSection>
<DataTable>
<DataTableHeader />
<DataTableBody>
<DataTableSkeleton />
<DataTableEmptyBody />
</DataTableBody>
</DataTable>
<DataTablePagination />
</DataTableRoot>

Full TypeScript support throughout:

type User = {
id: string
name: string
email: string
}
const columns: DataTableColumnDef<User>[] = [
{
accessorKey: "name", // ✅ Type-safe
header: "Name",
cell: ({ row }) => row.original.email, // ✅ Autocomplete
},
]
Feature This DataTable Component Libraries
Customization ✅ Full control ❌ Limited
Bundle size ✅ Copy what you need ❌ Entire library
Learning curve ✅ See the source ❌ Read docs
Dependencies ✅ Standard deps only ❌ Library-specific
Updates ✅ Copy new features ❌ Breaking changes
Feature This DataTable From Scratch
Time to implement ✅ Minutes ❌ Days/weeks
Accessibility ✅ Built-in ❌ Must implement
Mobile support ✅ Ready ❌ Must build
Advanced features ✅ Available ❌ Complex to build
Best practices ✅ Included ❌ Must learn

Ready to get started?

  1. Installation - Set up your project
  2. Simple Table - Your first table
  3. Basic Table - Add common features
  4. Search Table - Add search
  5. Faceted Filter Table - Advanced filtering
  6. Advanced Table - Master level

Questions? Ideas? Contributions?

Built on top of:

  • TanStack Table by Tanner Linsley - The headless table library that powers everything
  • Shadcn UI by Shadcn - Beautiful, accessible component primitives
  • sadmann7’s work - Major inspiration for filter components and table patterns:
    • TableCN - Inspired our filter menu, inline filter, faceted filter, and slider filter implementations
    • DiceUI Sortable - Drag and drop sortable for row reordering
  • nuqs by François Best - Type-safe search params state manager for URL state management
  • Web Dev Simplified Registry by Kyle Cook - Registry implementation pattern

MIT License - use it freely in your projects!