Use when building any sortable, filterable, paginated, selectable, or column-toggleable table in shadcn ui : the official shadcn DataTable is NOT a primitive component but a documented recipe that composes TanStack Table v8 (@tanstack/react-table) with the shadcn Table primitive. Use this skill any time the task involves columns of data, row selection with bulk actions, pagination of a list, server-side data loading with sorting or filtering, column-visibility toggles, or resizable columns. Prevents the recurring bugs : forgetting getCoreRowModel and getting zero rows, mixing client-side and server-side state so sorting fires twice, missing getRowId so row-selection keys collide, setting manualPagination without pageCount so pagination breaks, forgetting 'use client' on the DataTable component, defining a ColumnDef without accessorKey or accessorFn so cells render undefined, and importing TanStack helpers from the wrong package. Covers the ColumnDef typing pattern, the useReactTable hook composition, the six
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Use when building any sortable, filterable, paginated, selectable, or column-toggleable table in shadcn ui : the official shadcn DataTable is NOT a primitive component but a documented recipe that composes TanStack Table v8 (@tanstack/react-table) with the shadcn Table primitive. Use this skill any time the task involves columns of data, row selection with bulk actions, pagination of a list, server-side data loading with sorting or filtering, column-visibility toggles, or resizable columns. Prevents the recurring bugs : forgetting getCoreRowModel and getting zero rows, mixing client-side and server-side state so sorting fires twice, missing getRowId so row-selection keys collide, setting manualPagination without pageCount so pagination breaks, forgetting 'use client' on the DataTable component, defining a ColumnDef without accessorKey or accessorFn so cells render undefined, and importing TanStack helpers from the wrong package. Covers the ColumnDef typing pattern, the useReactTable hook composition, the six feature row models (core, sorted, filtered, paginated, with row selection, with column visibility), the optional column-resizing setup, the server-side data contract (manualSorting plus manualFiltering plus manualPagination plus pageCount or rowCount), the reusable DataTable component composition with shadcn Table primitives, flexRender for dynamic cell content, and the 'use client' boundary rule. Keywords: shadcn data table, TanStack Table v8, @tanstack/react-table, ColumnDef, useReactTable, flexRender, sortable table, filterable table, paginated table, row selection, column visibility, column resizing, server-side data table, manualSorting, manualFiltering, manualPagination, pageCount, rowCount, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, accessorKey, accessorFn, SortingState, ColumnFiltersState, VisibilityState, getRowId, enableRowSelection, 'use client' DataTable, how do I make a sortable table, how do I add pagination to a table, how do I select rows in a table, table is empty no rows showing, sorting fires twice, page count infinite, my checkboxes select the wrong row, cells are undefined, DataTable recipe, bulk actions table, why are my rows not rendering.
license
MIT
compatibility
Designed for Claude Code. Requires shadcn ui evergreen-2026 with TanStack Table v8 (@tanstack/react-table).
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
shadcn ui : DataTable Recipe (TanStack Table v8)
Deterministic mental model for the shadcn DataTable. There is NO DataTable primitive in shadcn. The official recipe (https://ui.shadcn.com/docs/components/radix/data-table) composes @tanstack/react-table v8 with the shadcn Table primitive. ALWAYS treat DataTable as a recipe-skill, not a primitive-skill : you write a reusable components/ui/data-table.tsx file in user space.
Markup : a <DataTable /> component renders the shadcn <Table> primitive driven by table.getHeaderGroups() and table.getRowModel().rows.
ALWAYS pass getCoreRowModel: getCoreRowModel() or the table renders ZERO rows. ALWAYS mark the DataTable component file "use client" ; the headless engine uses React hooks and breaks under server-rendering.
ALWAYS install the table primitive first ; the recipe imports Table, TableBody, TableCell, TableHead, TableHeader, TableRow from @/components/ui/table.
Six features, six row-model functions
Feature
Row model fn
State key
Setter prop
Render rows
getCoreRowModel()
:
:
Sort
getSortedRowModel()
sorting: SortingState
onSortingChange
Filter
getFilteredRowModel()
columnFilters: ColumnFiltersState
onColumnFiltersChange
Paginate
getPaginationRowModel()
pagination: PaginationState
onPaginationChange
Select rows
getCoreRowModel() (no extra fn)
rowSelection: RowSelectionState
onRowSelectionChange
Toggle columns
getCoreRowModel() (no extra fn)
columnVisibility: VisibilityState
onColumnVisibilityChange
Client vs server data : the single most important decision
Question
Answer
Dataset under ~1000 rows AND loaded once ?
Client-side. Pass full array to data. NO manualX flags.
Dataset over 1000 rows OR loaded per page ?
Server-side. Set manualSorting: true, manualFiltering: true, manualPagination: true. Provide pageCount (or rowCount). Refetch in a useEffect on state change.
Mixed (server pagination, client sort) ?
RARE. Only enable the manualX flag for what you actually compute server-side. Mismatch causes sorting to fire twice.
NEVER mix client-side and server-side for the SAME feature. If manualSorting: true is set, the engine STOPS sorting client-side ; you MUST refetch sorted data from the server.
'use client' boundary
ALWAYS put "use client" at the top of components/ui/data-table.tsx. The TanStack hook + the state hooks (sorting, filters, etc.) need a client component. The page that USES <DataTable /> may stay a server component as long as it imports <DataTable /> from a client module.
ALWAYS use flexRender(cell.column.columnDef.cell, cell.getContext()) in the body markup so dynamic cells render correctly. NEVER read cell.value directly.
ALWAYS provide pageCount (or rowCount) when manualPagination: true. Without it, the engine defaults pageCount to -1 and "next page" is infinite.
ALWAYS refetch on sorting, columnFilters, and pagination change. Use useQuery from TanStack Query, useEffect, or any data-fetching hook that takes these as dependencies.
NEVER add getSortedRowModel(), getFilteredRowModel(), or getPaginationRowModel() when their manualX flag is true ; the engine will sort/filter/paginate twice (once in your fetch, again in the model) and the visible order will not match what was fetched.
Client vs server decision tree
Is the full dataset already in memory ?
|--- YES -> client-side, add the get*RowModel for each feature.
|--- NO -> server-side, set manualX flags, provide pageCount,
refetch on state change.
DataTable Component Composition
ALWAYS extract the composition into components/ui/data-table.tsx for reuse. The component is generic over <TData, TValue>.
ALWAYS render the empty-state row (No results.) so the table never collapses to zero height. ALWAYS spread additional state and setters as props if the parent owns sorting/filtering/selection.
shadcn-syntax-table : the primitive <Table> / <TableHeader> / <TableBody> HTML wrapper this recipe renders into. READ FIRST if you are unsure of the primitive markup.
shadcn-syntax-selectors : the controlled <Select> used for page-size dropdowns inside DataTable pagination footers.
shadcn-syntax-menu-primitives : the <DropdownMenu> used to drive the column-visibility toggle.
Detailed References
references/methods.md : full ColumnDef, useReactTable, and per-feature signatures.
references/examples.md : six worked recipes (minimal sortable, filter + paginate, row selection + bulk actions, server-side with Next.js, column visibility toggle, custom cell renderers).
references/anti-patterns.md : six concrete failure modes with fixes.