Prodular UI

Data display

Data tablePro

A full data grid on TanStack Table v9: sorting, search, faceted filters with chips, column visibility, row selection with a bulk action bar, pagination, and loading and empty states.

npx prodular-ui add data-table

Part of Prodular UI Pro. Preview and read the docs freely, install with a license. See pricing

Invoices

Search, Status and Owner filters with live counts, Plan under Add filter, sortable headers, row actions and a bulk bar that appears when rows are selected. Shift-click selects a range.

INV-2095
Meridian Freightinvoices@meridianfreight.co
Draft
HSHana Sato
Nov 20, 2026$622.00
INV-2079
Orchard Lane Grocersap@orchardlane.store
Open
TLTheo Lindqvist
Nov 15, 2026$2,394.00
INV-2096
Halcyon Roboticsap@halcyonrobotics.ai
Paid
PRPriya Raman
Nov 11, 2026$11,227.00
INV-2057
Ironclad Securityfinance@ironclad.sh
Paid
HSHana Sato
Nov 11, 2026$18,647.00
INV-2066
Fable & Finchhello@fableandfinch.com
Open
TLTheo Lindqvist
Nov 9, 2026$56,324.00
INV-2076
Northwind Logisticsbilling@northwind.io
Paid
PRPriya Raman
Nov 8, 2026$4,426.00
INV-2069
Lumen Analyticsbilling@lumenanalytics.io
Open
DFDaniel Ferreira
Nov 7, 2026$3,591.00
INV-2090
Fable & Finchhello@fableandfinch.com
Open
PRPriya Raman
Nov 6, 2026$37,779.00
INV-2072
Halcyon Roboticsap@halcyonrobotics.ai
Open
HSHana Sato
Nov 3, 2026$22,949.00
INV-2092
Pinecrest Schoolsfinance@pinecrest.edu
Open
HSHana Sato
Oct 27, 2026$495.00

1โ€“10 of 46

Page 1 of 5

Sticky header, loading and empty

The header stays visible while the body scrolls. Loading shows skeleton rows in the real column layout, so nothing jumps when data arrives.

Weekly active usage
Northwind LogisticsMid-marketHealthy347$15,544
Kestrel HealthEnterpriseAt risk85$49,670
Brightline StudioSMBHealthy330$137,409
Ostara FoodsMid-marketHealthy254$104,459
Halcyon RoboticsEnterpriseChurning490$38,801
Meridian FreightSMBAt risk308$40,393
Cobalt Ridge CapitalMid-marketAt risk102$143,623
Lumen AnalyticsEnterpriseHealthy440$13,189
Pinecrest SchoolsSMBChurning364$54,903
Tidewater EnergyMid-marketAt risk243$34,201
Fable & FinchEnterpriseHealthy328$68,084
Granite Peak OutdoorsSMBHealthy52$9,329
Verity LegalMid-marketAt risk399$144,411
Sundial HospitalityEnterpriseHealthy13$8,861
Arcwell MedicalSMBHealthy284$109,047
Quill & Ink PressMid-marketAt risk470$118,181
Riverbend Credit UnionEnterpriseHealthy201$34,252
Helio Solar Co.SMBHealthy95$37,687
Mosaic LearningMid-marketChurning349$110,712
Ironclad SecurityEnterpriseHealthy392$100,352

Anatomy

const helper = createDataTableColumnHelper<Invoice>()const columns = helper.columns([  dataTableSelectColumn<Invoice>(),  helper.accessor("status", {    header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />,    filterFn: "facet",  }),  helper.accessor("amount", { header: "Amount", meta: { align: "end" } }),])
const table = useDataTable({ data, columns, getRowId: (r) => r.id })
<DataTable table={table}>  <DataTableToolbar>    <DataTableSearch placeholder="Search invoices" />    <DataTableFilterBar filters={filters} />    <DataTableToolbarSpacer />    <DataTableViewOptions />  </DataTableToolbar>  <DataTableFilterChips filters={filters} />  <DataTableContent>    <DataTableBulkActions>      <Button size="sm">Mark as paid</Button>    </DataTableBulkActions>  </DataTableContent>  <DataTablePagination /></DataTable>

Guidance

Use it when

  • Lists of records people scan, compare and act on: invoices, customers, deployments, tickets.
  • Up to a few thousand rows on the client. Beyond that, switch on manualSorting, manualFiltering and manualPagination and let the server do the work.
  • Faceted filters for columns with a small set of known values (status, owner, plan). Search for free text.

Avoid

  • Wrapping the table in another Card. DataTableContent already is the surface.
  • More than one primary button in the bulk action bar.
  • Faceted filters on columns with hundreds of unique values. Use search, or a dedicated combobox.
  • Center-aligned numbers. Amounts, counts and comparable dates are right-aligned with meta.align set to end.

Rules for AI agents

Published in llms-full.txt and the registry, so coding agents follow them when they build with this component.

  • Create columns with createDataTableColumnHelper<TData>() and helper.columns([...]) at module scope, never inline in render.
  • Always pass getRowId with a stable id from the data. Index ids break selection after sorting.
  • Set filterFn: "facet" on every column used by DataTableFacetedFilter or DataTableFilterBar, and pass the same filters config to DataTableFilterChips.
  • Set meta.align to "end" on money, counts and dates people compare. Set meta.label when the header is a component.
  • Use dataTableSelectColumn<TData>() for selection instead of writing checkbox cells. Put DataTableBulkActions inside DataTableContent.
  • Render status columns with Status appearance="pill", people with Avatar plus name, and ids in font-mono at 13px.
  • Pass loading while fetching instead of swapping in a spinner. Do not wrap DataTableContent in a Card.
  • Give each DataTableRowActions a label that names the record.

API

useDataTable

PropTypeDefaultDescription
dataTData[]Rows. Keep the array stable (state or memo); a new array every render recomputes every row model.
columnsColumnDef[]Build with createDataTableColumnHelper<TData>().columns([...]) at module scope.
getRowId(row: TData) => stringStable ids keep selection correct across sorting and pages. Always pass it.
initialStatePartial<TableState>{ pagination: { pageIndex: 0, pageSize: 10 } }Starting sort, filters, hidden columns and page size.

DataTable

PropTypeDefaultDescription
tableDataTableInstanceThe instance from useDataTable. Every part below reads it from context.

DataTableContent

PropTypeDefaultDescription
loadingbooleanfalseShows skeleton rows in place of data and sets aria-busy.
stickyHeaderbooleanfalsePins the header row. Use with maxHeight.
maxHeightnumber | stringHeight of the scroll area.
emptyReactNodeReplaces the default empty state. The default adapts to whether filters are active.
onRowClick(row) => voidMakes rows clickable. Clicks on the checkbox and actions cells are ignored.

DataTableFilterBar

PropTypeDefaultDescription
filtersDataTableFilterConfig[]{ column, title, options, pinned }. Pinned filters always show; the rest sit under Add filter until used.

DataTableFilterChips

PropTypeDefaultDescription
filtersDataTableFilterConfig[]Pass the same config so chips show titles and option labels.

DataTableColumnHeader

PropTypeDefaultDescription
column / titleColumn / stringA sort button with a direction icon. Sets aria-sort on the header cell.

DataTablePagination

PropTypeDefaultDescription
pageSizesnumber[][10, 20, 50, 100]Options for Rows per page.

DataTableRowActions

PropTypeDefaultDescription
labelstring"Row actions"Accessible name of the trigger. Include the record, for example Actions for INV-2041.

Column meta

PropTypeDefaultDescription
align"start" | "end"end right-aligns header and cells and turns on tabular numbers.
labelstringName used in the View menu and filter chips when the header is a component.