budtags/skills/tanstack-table/SKILL.md
Use when working with TanStack Table for data tables, datagrids, sorting, filtering, pagination, row selection, column customization, or virtualization. Load specific pattern files based on the feature needed.
npx skillsauth add jwilly246/budtags-claude-plugin tanstack-tableInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in TanStack Table v8+, the headless UI library for building powerful, flexible data tables and datagrids. This skill provides comprehensive documentation, patterns, and BudTags/BobLink-specific examples for implementing table functionality.
When this skill is active, you can:
patterns/01-installation-setup.md (~150 lines) - Installation, dependencies, basic setuppatterns/02-core-concepts.md (~200 lines) - Headless UI philosophy, table instance, state managementpatterns/03-column-definitions.md (~250 lines) - Column types, column helpers, accessor patternspatterns/04-table-instance.md (~180 lines) - Creating tables, options, state, methodspatterns/05-row-models.md (~200 lines) - Core, filtered, sorted, grouped, paginated row modelspatterns/06-rendering.md (~220 lines) - Headers, cells, rows, flexRender, custom componentspatterns/07-sorting.md (~180 lines) - Sorting setup, multi-sort, custom sort functionspatterns/08-filtering.md (~250 lines) - Column filters, global filter, custom filter functionspatterns/09-pagination.md (~170 lines) - Page state, page controls, manual paginationpatterns/10-row-selection.md (~200 lines) - Selection state, checkboxes, select all patternspatterns/11-column-visibility.md (~160 lines) - Show/hide columns, visibility togglespatterns/12-column-ordering.md (~150 lines) - Drag & drop columns, reorderingpatterns/13-column-sizing.md (~180 lines) - Resizable columns, min/max widthspatterns/14-column-pinning.md (~150 lines) - Pin left/right, sticky columnspatterns/15-row-expansion.md (~190 lines) - Expandable rows, sub-rows, nested datapatterns/16-row-grouping.md (~200 lines) - Group by column, group headerspatterns/17-aggregation.md (~180 lines) - Aggregate functions, grouped aggregationpatterns/18-row-pinning.md (~140 lines) - Pin rows to top/bottompatterns/19-virtualization.md (~200 lines) - Virtual scrolling, large datasetspatterns/20-faceted-filtering.md (~170 lines) - Faceted search, filter countspatterns/21-custom-features.md (~180 lines) - Plugin system, custom featurespatterns/22-typescript.md (~160 lines) - Type safety, generics, type inferencepatterns/23-performance.md (~190 lines) - Optimization tips, memoizationpatterns/24-api-reference.md (~300 lines) - Complete API listingTotal: ~4,480 lines of progressive disclosure documentation
npm install @tanstack/react-table
import {
createColumnHelper,
flexRender,
getCoreRowModel,
useReactTable,
} from '@tanstack/react-table'
type Package = {
Tag: string
ProductName: string
Quantity: number
UnitOfMeasureName: string
}
const columnHelper = createColumnHelper<Package>()
const columns = [
columnHelper.accessor('Tag', {
header: 'Package Tag',
cell: info => info.getValue(),
}),
columnHelper.accessor('ProductName', {
header: 'Product',
}),
columnHelper.accessor('Quantity', {
header: 'Quantity',
cell: info => {
const quantity = info.getValue()
const unit = info.row.original.UnitOfMeasureName
return `${quantity} ${unit}`
},
}),
]
function PackagesTable({ data }: { data: Package[] }) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
return (
<table>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th key={header.id}>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => (
<tr key={row.id}>
{row.getVisibleCells().map(cell => (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
))}
</tr>
))}
</tbody>
</table>
)
}
import {
getCoreRowModel,
getSortedRowModel,
getFilteredRowModel,
useReactTable,
} from '@tanstack/react-table'
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
})
// In header rendering:
<th onClick={header.column.getToggleSortingHandler()}>
{flexRender(header.column.columnDef.header, header.getContext())}
{{
asc: ' 🔼',
desc: ' 🔽',
}[header.column.getIsSorted() as string] ?? null}
</th>
Load only what you need:
BudTags Implementation:
// resources/js/Components/DataTable.tsx
export function DataTable<TData>({
data,
columns,
enableSorting = true,
enableFiltering = true,
enablePagination = true,
}: DataTableProps<TData>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: enableSorting ? getSortedRowModel() : undefined,
getFilteredRowModel: enableFiltering ? getFilteredRowModel() : undefined,
getPaginationRowModel: enablePagination ? getPaginationRowModel() : undefined,
})
return (
<BoxMain>
{/* Render table */}
</BoxMain>
)
}
From TableHelpers.tsx:
export function createCheckboxColumn<TData>(
options?: {
enableSelectAll?: boolean
}
): ColumnDef<TData> {
return {
id: 'select',
header: ({ table }) => (
options?.enableSelectAll ? (
<input
type="checkbox"
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
) : null
),
cell: ({ row }) => (
<input
type="checkbox"
checked={row.getIsSelected()}
disabled={!row.getCanSelect()}
onChange={row.getToggleSelectedHandler()}
/>
),
}
}
From TableHelpers.tsx:
export function createFilterButtonCell<TData, TValue>(
accessor: (row: TData) => TValue,
filterKey: string
) {
return {
cell: ({ getValue, table }: CellContext<TData, TValue>) => {
const value = getValue()
return (
<button
onClick={() => {
table.getColumn(filterKey)?.setFilterValue(value)
}}
className="text-blue-600 hover:underline"
>
{String(value)}
</button>
)
},
}
}
From TableHelpers.tsx:
export function createDateSortingFn() {
return (rowA: Row<any>, rowB: Row<any>, columnId: string) => {
const dateA = new Date(rowA.getValue(columnId))
const dateB = new Date(rowB.getValue(columnId))
return dateA.getTime() - dateB.getTime()
}
}
Files: TablePackages.tsx, TablePackagesActive.tsx
Features Used:
Files: TableOrdersLeaflink.tsx
Features Used:
Files: TablePlants.tsx
Features Used:
const columnHelper = createColumnHelper<Package>()
const columns = [
columnHelper.accessor('Tag', {
header: 'Tag',
// ✅ Type-safe: Tag is string
}),
]
const columns = [
{
accessorKey: 'Tag', // ❌ No type checking
header: 'Tag',
},
]
const columns = useMemo(() => [
columnHelper.accessor('Tag', { ... }),
], [])
// ❌ Creates new column instances every render
const columns = [columnHelper.accessor('Tag', { ... })]
{flexRender(cell.column.columnDef.cell, cell.getContext())}
{cell.column.columnDef.cell} {/* ❌ Won't work with functions */}
const table = useReactTable({
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(), // ✅ Enable sorting
getFilteredRowModel: getFilteredRowModel(), // ✅ Enable filtering
})
const table = useReactTable({
getCoreRowModel: getCoreRowModel(),
// ❌ Missing getSortedRowModel - sorting won't work
})
Problem: Table rerenders unnecessarily because data array is recreated
Solution: Memoize data
const data = useMemo(() => packages, [packages])
Problem: Features don't work (sorting, filtering, pagination)
Solution: Add required row models
getSortedRowModel: getSortedRowModel() // For sorting to work
Problem: cell.column.columnDef.cell is a function but rendered as string
Solution: Use flexRender
{flexRender(cell.column.columnDef.cell, cell.getContext())}
Problem: Table lags with 1000+ rows
Solution: Use virtualization
import { useVirtualizer } from '@tanstack/react-virtual'
// See pattern 19 for full implementation
const { data: packages } = useQuery({
queryKey: ['packages', license],
queryFn: () => fetchPackages(license),
})
const table = useReactTable({
data: packages ?? [],
columns,
getCoreRowModel: getCoreRowModel(),
})
const table = useReactTable({ /* ... */ })
const { rows } = table.getRowModel()
const rowVirtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => tableContainerRef.current,
estimateSize: () => 50,
})
When helping with TanStack Table:
Remember: TanStack Table is already used extensively in BudTags. Your goal is to help implement new features, optimize existing tables, and solve table-related challenges using the comprehensive documentation in this skill.
Load specific pattern files as needed using progressive disclosure. For basic setup, load patterns 01-06. For specific features, load the corresponding pattern file.
development
Use this skill when generating ZPL code, working with ZPL commands, creating Zebra printer labels, or troubleshooting ZPL syntax and formatting issues.
development
Use this skill to verify that code aligns with BudTags coding standards, architectural patterns, and conventions before or after implementation.
development
Use this skill when working with Unleashed Software inventory/order management API integration, syncing inventory, importing orders, managing stock adjustments, or handling customer/product data from Unleashed.
data-ai
TanStack Virtual patterns for virtualized lists, tables, and grids with high-performance rendering of large datasets