Shadcn React Table

Search documentation

Search the docs

Updating your copy

shadcn add copies this block's source into your project — after that, the code is yours, and upstream releases don't reach it automatically. This page is the manual update path.

Know what you have

Every release stamps its version into the registry item. When you install, note the meta.version of /r/data-table.json somewhere in your project (a comment, your PR description). The changelog lists what changed in each version.

Update recipe

  1. Start from a clean branch.

  2. Re-run the install command with --overwrite:

    bash
    npx shadcn@latest add --overwrite <registry-url>/r/data-table.json
    
  3. Review the diff. Everything under components/ui/data-table/ is replaced with the new upstream source — git diff shows upstream changes mixed with the loss of any local edits you made.

  4. Re-apply your local edits on top, or cherry-pick hunks with git checkout -p against the pre-update commit.

  5. Check the changelog for entries marked as breaking and adjust your usage.

Keep local edits separate

The less you edit the copied files directly — prefer wrapping the table or using the many render* slots — the closer step 3's diff is to a pure upstream update.

Localization keys and installed locales

When a release adds localization keys, re-add every locale you installed, otherwise its typecheck fails on the missing keys:

bash
pnpm dlx shadcn@latest add --overwrite <registry-url>/r/data-table-locale-de.json

Do this after re-adding the table, and re-apply any edits you made to a locale file.

Upgrading to 1.0.0

1.0.0 moves the engine to TanStack Table v9 and settles the API. Re-add the block, then:

  1. Type columns with DataTableColumnDef. TanStack v9's ColumnDef needs a feature-set type parameter; ours supplies it.

    ts
    import type { DataTableColumnDef } from "@/components/ui/data-table"
    const columns: DataTableColumnDef<Person>[] = [...]
    

    DataTableRow, DataTableColumn, DataTableCell, and DataTableHeader are the matching instance types. Install @tanstack/react-table@^9 and @tanstack/match-sorter-utils@^9 (the registry item pins both).

  2. Drop row-model options. getCoreRowModel, getSortedRowModel, … are no longer accepted; the table wires every row model itself.

  3. Custom aggregation functions use TanStack v9's constructAggregationFn({ aggregate }) shape. Built-in names ("sum", "mean", …) are unchanged.

  4. Instance APIs follow TanStack v9: column.pin("start" | "end"), column.getIsPinned() returns "start" | "end", getPrePaginatedRowModel(). Table options and table.getState() keep the left/right pinning shape, and table.getState().columnSizingInfo keeps its name. Other renames: getLeft*/getRight* are getStart*/getEnd* (leaf columns, visible cells, header groups, total size), column.getStart("left") and column.getAfter("right") take "start" / "end", and getPaginationRowModel() is getPaginatedRowModel().

  5. Option and column-def renames reach you too. The column option sortingFn is now sortFn. state.columnSizingInfo, initialState.columnSizingInfo and onColumnSizingInfoChange are now columnResizing and onColumnResizingChange (only the value returned by table.getState() keeps the columnSizingInfo name).

  6. filterFns, sortingFns and aggregationFns options are gone. The hook owns the feature set, so pass custom functions inline on the column (filterFn: myFilterFn, sortFn: myCompare, aggregationFn: myAgg). Built-in string names still work. Type inline filter functions with the exported DataTableFilterFn<TData>.

  7. Don't destructure row/column/cell methods (const { getValue } = row); call them on the object. Cell-renderer ({ getValue }) still works.

  8. Row data must satisfy TanStack's RowData (Record<string, any> | Array<any>). unknown is no longer accepted, so use a concrete row type for useDataTable<TData> and DataTableInstance<TData>. DataTableInstance with no type argument now defaults to RowData.

  9. Render contexts are unchanged. Column renderers (cell, header, footer, aggregatedCell) still receive the enriched instance, so ({ table }) => table.getState(), table.dataTable and the table's methods type-check. TanStack v9's own contexts don't include getState.

  10. table.options is the options object you passed in on the latest render (TanStack v9's React adapter behavior), not TanStack's internally merged options with feature defaults.

  11. Two small TanStack features are registered by the block. Their types add dataTableInstanceFeature and dataTableUiFeature through TanStack's Plugins / Table_FeatureMap / TableState_FeatureMap interfaces, scoped to tables built with the block's feature set. Other TanStack tables in your app are unaffected, except ones typed Table<any, …>.

  12. The component holding the table no longer re-renders on table state changes. If it reads the state while rendering (a selection count, the current density), add stateSelector for what it reads, or wrap that part in table.Subscribe. See State management.

API renames in 1.0.0

0.x1.0.0
table.tableInstance.setDensity(…) (and every setter or action)table.setDensity(…)
table.tableInstance.density (and other UI state)table.getState().density
table.tableInstance.isLoading (and other options, refs)table.dataTable.isLoading
DataTableConfig typeDataTableOptionsResolved (+ DataTableMethods)
defaultDensity: "compact"initialState: { density: "compact" }
density, isFullscreen, showColumnFilters, advancedFilter, globalFilterMode propsstate: { … }
defaultShowColumnFilters, defaultGlobalFilterMode, defaultAdvancedFilterinitialState: { … }
onEditCellSave / onSaveRow / onCreateRowonEditingCellSave / onEditingRowSave / onCreatingRowSave
renderToolbarActionsrenderTopToolbarCustomActions
meta.variant / meta.optionsmeta.filterVariant / meta.filterSelectOptions
meta.disableHighlight: truemeta.enableFilterMatchHighlighting: false
meta.disableColumnActions: truemeta.enableColumnActions: false
<DataTable pageSizeOptions={…} />useDataTable({ pageSizeOptions: … })
<DataTable surfaceClassName="…" />useDataTable({ slotProps: { surface: { className: "…" } } })
renderColumnActionsMenuItems appended after a separatorreturn <>{internalMenuItems}<DropdownMenuSeparator />{yours}</>
Edit button next to the renderRowActionMenuItems menuEdit is in the menu: render internalMenuItems in your items
sortingFns optionsortFns