Skip to content
Niko Table

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, this is not a component library. It is a registry of DataTable* components that you copy into your project and customize. Built with TanStack Table and Shadcn UI — enterprise-grade features, fully under your control. niko-table is the registry name (@niko-table/...); the components you mount are DataTable, DataTableRoot, and friends.

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

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 your shadcn generation’s primitives (Radix UI or Base UI) 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/ui 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 read from the TanStack Table instance (like table.state, 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
// `features` comes from @/components/niko-table/lib/data-table-features
const table = useTable({ features, 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>

DataTable 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. TanStack Table v9 matches that at the engine: v8 bundled every behavior into useReactTable; v9 lets you import only what you need via lib/data-table-features.ts (the file you trim after install, same as omitting components you never mount).

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 - shadcn/ui primitives (Radix or Base UI), 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@^9 - Core table library (v9: register features in lib/data-table-features.ts)
  • 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 — and register only the TanStack features those pieces need. Registry installs and lib/data-table-features.ts are the same principle at two layers:

// 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 ✅ Baseline included ❌ 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!