Data Table
A full-featured data table built on TanStack Table: sticky column pinning, row virtualization for large datasets, dnd-kit drag-to-reorder rows, a paginated footer, a filter toolbar with text / number / range (slider) / date / faceted (select) filters driven by per-column meta, a column-visibility view menu, sortable column headers, and a loading skeleton. Bring your own TanStack table instance; the pieces are composable.
A full-featured data table built on TanStack Table.
You own the table instance (columns, models, state); ikui gives you the
composable UI around it: a scroll container with sticky column pinning, opt-in
row virtualization for large datasets, dnd-kit drag-to-reorder rows, a
paginated footer, and a filter toolbar driven by per-column meta.
The toolbar reads each column's meta.variant and renders the matching control —
text, number, range (a slider popover), date / dateRange (a calendar
popover), and select / multiSelect (a faceted command popover) — alongside a
column-visibility view menu. Sortable headers, a loading skeleton, and a
row drag handle ship as separate pieces you compose as needed.
Installation
One-time setup: add the @ikui registry to your components.json.
{
"registries": {
"@ikui": "https://ik-ui.pages.dev/r/{name}.json"
}
}Then install the component:
pnpm dlx shadcn@latest add @ikui/data-tableUsage
Declare filters through columnDef.meta, then hand the table to DataTable with
DataTableToolbar as its child.
import {
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { DataTable } from "@/components/data-table";
import { DataTableToolbar } from "@/components/data-table-toolbar";
import { DataTableColumnHeader } from "@/components/data-table-column-header";
const columns = [
{
accessorKey: "title",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Title" />
),
meta: { label: "Title", variant: "text", placeholder: "Filter titles..." },
enableColumnFilter: true,
filterFn: "includesString",
},
{
accessorKey: "status",
header: "Status",
meta: {
label: "Status",
variant: "select",
options: [
{ label: "Todo", value: "todo" },
{ label: "Done", value: "done" },
],
},
enableColumnFilter: true,
filterFn: "arrIncludesSome",
},
];
function Example() {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return (
<DataTable table={table}>
<DataTableToolbar table={table} />
</DataTable>
);
}Column meta
The toolbar and filters read these fields off columnDef.meta (types are added
by data-table-utils.ts via module augmentation):
| Prop | Type | Default |
|---|---|---|
label | string | - |
placeholder | string | - |
variant | "text" | "number" | "range" | "date" | "dateRange" | "select" | "multiSelect" | - |
options | { label; value; count?; icon? }[] | - |
range | [number, number] | - |
unit | string | - |
Props
| Prop | Type | Default |
|---|---|---|
table | Table<TData> | - |
enableVirtualization | boolean | true |
estimateRowSize | number | 80 |
enableDragAndDrop | boolean | false |
onDragEnd | (event: DragEndEvent) => void | - |
getRowId | (row: TData) => number | string | - |
showPagination | boolean | true |
totalCount | number | - |
actionBar | React.ReactNode | - |
footer | React.ReactNode | - |
emptyState | React.ReactNode | - |