Changelog
All notable changes to @ackplus/mui-tanstack-data-grid. This project follows
Semantic Versioning and the Keep a Changelog
format: each release groups changes as Added (new features), Changed (behaviour/build), Fixed
(bug fixes), and Docs.
Upgrading is safe within a major version —
1.xreleases are backward compatible. Install a specific version withnpm install @ackplus/mui-tanstack-data-grid@<version>.
1.17.1 — 2026-06-21
Fixed
- Select/boolean editors in whole-row edit mode wouldn't open — the dropdown was rendered with a
controlled
open={false}, so clicking it did nothing. It's now uncontrolled in row mode (click to open, pick to buffer, Save commits with the row). The Editing row-mode example now shows aselectcolumn end-to-end.
1.17.0 — 2026-06-21
Editing gains whole-row mode and custom editors. Backward compatible (editMode defaults to 'cell').
Added
- Whole-row edit mode. Set
editMode="row"and a row's editable cells open together with explicit Save / Cancel in the actions column (auto-added — no wiring needed);processRowUpdatefires once per row with the fully-updated row. Start via double-click / Enter /apiRef.current.editing.startRowEdit(id);onRowEditStart/onRowEditStopcallbacks; the actions are also exported ascreateRowEditActionto mix with your owngetRowActions. - Custom cell editors.
columnDef.editComponentnow actually drives the inline editor (it was previously dead). It receivesDataTableEditComponentProps(value/onChange/onCommit/onCancel/row/column/align/editMode) and works in both cell and row mode.
Changed
columnDef.editComponentis no longer a fallback for the column filter input — usefilterComponentfor that. (No known consumers relied on the fallback.)
Docs
- Editing documents row mode, the
editComponenteditor, andapiRef.current.editing.
1.16.1 — 2026-06-21
Inline-editing fixes + much fuller docs.
Fixed
- Nested-key edits. Committing an edit to a dot-nested
accessorKey(e.g.address.city) now deep-sets the nested field instead of creating a stray flat"address.city"key, so the change actually takes effect. NewsetNestedValueutil (immutable, clones only along the path). - Editor UI. Dropped the inline editor's underline (it rendered as a stray line across the middle of the cell); the cell now shows a clean full-cell ring while editing.
Docs
- Editing is rewritten to cover getting the updated data (
processRowUpdate), which field is written (accessorKey / nested keys / row identity), server-side persistence, validation, the imperativeapiRef.current.dataAPI, and the type-aware editors.
1.16.0 — 2026-06-21
Last remaining backlog slice — two rendering-correctness fixes. Backward compatible.
Added
- Right-to-left (RTL). Set
direction: 'rtl'on your theme and the grid mirrors itself — pinned columns flip side, the resize handle and sort/⋮-menu move to the logical edge, text aligns to the start, and tree indentation + column separators mirror. The grid setsdir="rtl"on its root, so it works with no extra setup. See Theming → RTL.
Fixed
- Grouped (multi-level) headers now align correctly: a group header spans its leaf columns' combined
width and lines up with the leaf row and body cells (previously they could drift under
fitToScreen). Group headers carry no sort/resize/⋮-menu, only the leaf header row takes keyboard focus, andaria-rowindex/aria-rowcount/aria-colspanaccount for the extra header rows. See Columns → Grouped headers.
Docs
1.15.0 — 2026-06-21
Third remaining backlog slice. Backward compatible.
Added
- Saved / named views.
enableSavedViewsadds a toolbar Views control that captures the current layout (search, filters, sort, column visibility/order/width, pinning, density, page size) as a named preset the user can switch between, update, and delete. A dot marks unsaved changes against the active view. Stored underdt:<stateKey>:views(uncontrolled) or driven viaviews/onViewsChange/activeViewId/onActiveViewChange(controlled). FullapiRef.current.viewsnamespace (saveView/applyView/updateView/renameView/deleteView/resetView/listViews/getActiveView/isDirty) and aviewsIconslot.
Changed
apiRef.current.layout.saveLayout()/restoreLayout()now also capture and restore sorting and density (so a layout snapshot — and a saved view — round-trips the full user-visible layout).
Docs
- New Saved views page with a live demo.
1.14.0 — 2026-06-21
Second remaining backlog slice. Backward compatible.
Added
- Row pinning. Pin individual rows to the top and/or bottom so they stay visible while the
rest scroll — pinned rows survive sort, filter, and pagination. Enable with
enableRowPinning; seed withinitialState.rowPinning; pin viaapiRef.current.rowPinning(pinRowTop/pinRowBottom/unpinRow/setRowPinning/resetRowPinning/getPinnedRowIds) or the exportedcreateRowPinActionhelper (spread intogetRowActions). FiresonRowPinningChange; included insaveLayout/restoreLayout. Client data mode only (ignored under server pagination); top-level rows only (not tree sub-rows). Persist withpersist.include: ['rowPinning']plus a stablegetRowId.
Docs
- Pinning gains a "Row pinning" section + live demo.
1.13.0 — 2026-06-21
First of the remaining backlog slices. Backward compatible.
Added
- Column header menu. Each column header gains a quiet ⋮ menu (revealed on hover or keyboard
focus) that surfaces the actions already wired into the grid — sort ascending/descending/clear,
autosize this column (needs
enableColumnResizing), and hide column (needsenableColumnVisibility). It only lists available actions (none → no kebab), marks the active sort, disables redundant items, and won't hide the last visible column. Keyboard: focus a header and press Enter. On by default —enableColumnMenu={false}removes it table-wide,disableColumnMenu: trueper column; swap the glyph withslots.columnMenuIcon.
Changed
- With the column menu on (the default), pressing Enter on a focused sortable header now opens its
column menu instead of toggling the sort directly (the menu's first item is Sort ascending).
Mouse-clicking the header still toggles sort. Set
enableColumnMenu={false}to restore the old behavior.
Docs
- Columns gains a "Column menu" section + live demo.
1.12.0 — 2026-06-21
More admin power-user features from the deferred backlog. Backward compatible.
Added
- Configurable page size.
rowsPerPageOptionssets the footer's page-size choices (default[5, 10, 25, 50, 100];[]hides the selector). Replace the whole footer withslots.pagination. - Faceted (auto) filter options. A
type: 'select'column with nooptionsauto-populates its filter values from the data's distinct values (cardinality-guarded). Values reflect the data, not other active filters. - Clipboard copy.
enableClipboardCopyadds a Copy action to the bulk-actions bar (tab-separated text that pastes into Sheets/Excel); alsoapiRef.current.clipboard.copySelectedRows()and theonClipboardCopy(count)callback.
Docs
- Pagination (page-size options), Filtering (auto-populated options), and Selection (clipboard copy) each gain a section + live demo.
1.11.1 — 2026-06-21
Fixed
- Client-mode counts now respect active filters. When filtering client-side, the pagination footer
("of N"),
aria-rowcount, the select-all / exclude-mode count, and the last-page math used the unfiltered total. They now reflect the filtered (pre-pagination) row count. Server mode, therowCounttable option, and the export pipeline are unchanged.
1.11.0 — 2026-06-20
Polishes the Tier C features — the documented follow-ups. Backward compatible.
Added
- Screen-reader announcements. A visually-hidden
aria-liveregion announces sort, filter, page, and selection changes (localizable:announceSort,announceFilteredRows,announcePage,selectedRows). - Persistable tree expansion. Add
'expanded'topersist.includeto remember which tree rows are expanded across reloads (opt-in; still excluded by default).
Fixed
- Column filters are now tree-aware. A branch is kept when it or any descendant matches, so a matching child is no longer dropped because its parent doesn't match (previously filters were top-level-only). The kept branch shows in full.
Docs
- Accessibility documents the live-region announcer; Tree data documents tree-aware filtering and expansion persistence.
1.10.0 — 2026-06-20
Completes Tier C — keyboard accessibility, inline editing, and tree data. Backward compatible (no API removed); the grid now uses the ARIA grid roles and keyboard nav is always on.
Added
- Keyboard navigation + ARIA grid. WCAG grid-pattern operability:
role="grid"(treegridfor tree data),aria-rowindex/aria-colindex/aria-rowcount/aria-colcount, headeraria-sort. A roving tabindex with ↑ ↓ ← →, Home/End, Ctrl+Home/End, PageUp/PageDown moves the focused cell; Enter/F2 activates it (sort a header, toggle a control, or start editing); focus follows paging and scrolls virtualized rows into view. See Accessibility. - Inline cell editing. Mark a column
editable; double-click or Enter/F2 to edit. Type-aware editor (text / number / date, dropdown forselect/boolean); Enter or blur commits, Escape cancels.processRowUpdate(newRow, oldRow)persists the change (reverts +onProcessRowUpdateErroron a throw); without it, edits update the grid's local data. - Tree data.
getSubRowsrenders hierarchical rows — an expander only on rows with children, depth indentation, androle="treegrid"+aria-level/aria-expanded. Use instead ofrenderDetailPanel.
Changed
- The scroll container is now
role="grid"(wasrole="table") and body cellsrole="gridcell", per the ARIA grid pattern.
Docs
- New Accessibility, Editing, and Tree data feature pages, each with a live demo.
Finishes Tier B — a footer aggregation row and full localization. Backward compatible.
Added
- Footer aggregation.
enableAggregation+ a per-columnaggregation('sum' | 'avg' | 'min' | 'max' | 'count', or a function over the filtered rows) renders a sticky footer summary row, aligned with and pinned like its columns. Totals also available viaapiRef.current.aggregation.getTotals(). Client mode (server/grouped totals out of scope). - Localization —
localeText. Every built-in string — operator labels, toolbar tooltips, the column-filter and columns panels, the selection count, "No rows", pagination labels, and aria-labels — is translatable via a partiallocaleText(unset keys fall back to English). Operator labels localize while the underlying operator value (the logic key) is unchanged. The English bundle is exported asenUS/DEFAULT_LOCALE_TEXT; interpolated strings (selectedRows,paginationDisplayedRows) are functions.
Docs
- New Aggregation and Localization feature pages, each with a live demo (a summary row; an EN↔FR toggle).
1.8.0 — 2026-06-20
Tier B CRUD verbs — a row-actions column and value getters / formatters, the two features that remove the most per-column boilerplate. Backward compatible.
Added
- Row actions.
getRowActions(row) => DataTableRowAction[]adds an auto-generated_actionscolumn (pinned right whenenableColumnPinningis on). A few actions render as inline icon buttons; 3+ (or any without anicon) collapse into an overflow ⋮ menu. Each action is{ label, icon?, onClick(row), disabled?, hidden?, color? }. Configure viaslotProps.actionsColumn,rowActionsDisplay('icons' | 'menu' | 'auto'), andslots.moreActionsIcon. Handlers stop propagation so they don't triggeronRowClick. column.valueGetter— derive a value for a column with noaccessorKey; it compiles to a TanStackaccessorFn, so sorting, filtering, and export all read the derived value.column.valueFormatter— format a value for display only (export keeps the raw value).- Type-default cells. A column's
type(number/date/boolean/select) now renders a sensible default cell — locale-grouped number,dayjs-formatted date (no UTC drift), Yes/No, or the matchingoptionslabel — when you don't supply acell. Precedence:cell>valueFormatter> type default > raw.
Docs
- Columns gains Value getters & formatters and Row actions sections, each with a live demo.
1.7.0 — 2026-06-20
Makes the public API honest — turns several typed-but-dead props/methods into working features and adds a range filter operator. Backward compatible: every new hook is opt-in and unset behaves exactly as before.
Added
column.wrapText— wrap long cell (and header) text instead of truncating with an ellipsis. In non-virtualized grids the CSS-Grid rows grow to fit for free.column.cellClassName—stringor({ value, row }) => stringapplied to each body cell of the column (DataGrid-style conditional styling).column.headerClassName—stringapplied to the column's header cell.getRowClassName({ row, index })— table-level hook to class each row.getCellClassName({ row, columnId, value })— table-level hook to class each cell; merges with the per-columncellClassName.betweenfilter operator (number & date) in the column-filter rule builder — a two-field{ from, to }range; either bound may be omitted (open-ended). Inclusive.
Fixed
autoSizeColumn/autoSizeAllColumnsnow work (apiRef.columnResizing.*) — they were no-ops. Double-click a column's resize handle to fit it to its content; widths clamp to the column'sminSize/maxSizeand persist like a manual resize. (Measures the rows currently in the DOM — the virtual window / current page.)- Resized / auto-fitted columns hold their exact width under
fitToScreen. Previously a column you resized still flex-grew to fill, so its width drifted; now a user-sized column keeps its width while the remaining (untouched) columns absorb spare space.
Docs
- Columns gains a Cell & row styling section with a live demo of
wrapTextand the class-name hooks, and a note on double-click auto-fit. The Filtering page documents thebetweenrange operator.
:::note Virtualization + auto-height
wrapText grows rows in the standard (non-virtualized) grid. Under enableVirtualization the virtualizer
uses a fixed row-height estimate, so wrapped rows are clipped to the estimate; per-row measurement for
virtualized auto-height is a separate, future enhancement.
:::
1.6.1 — 2026-06-19
Fixes the height / sticky / scroll behaviour and gives it a proper, standard model — the grid is now a
flex column with three height modes (Height & scrolling). Backward compatible:
auto-height and existing stickyHeader / maxHeight setups are unchanged.
Fixed
stickyFooternow works. It was declared but never wired; it now pins the footer and bounds the body likestickyHeader.maxHeightis active on its own. Previously it was ignored unlessstickyHeader(orenableVirtualization) was also set; settingmaxHeightnow caps and scrolls by itself.enableStickyHeaderOrFooter(the deprecated alias) is honored again — it maps tostickyHeader.- The grid root is a flex column and the scroller flexes (
flex: 1 1 auto; min-height: 0), so in a fixed/fill height the footer correctly pins to the bottom while the body scrolls.
Added
height— fix the grid height ('100%'fills a sized parent); the body flexes to fill and scrolls while the header and footer stay pinned (the dashboard-panel layout that wasn't previously expressible).minHeight— optional floor so a near-empty grid doesn't collapse. A bounded height is not required for the body to scroll; this is purely a minimum.
Docs
- New Height & scrolling feature page with a live demo of each mode. The example app's
Layout page was rewritten around the three modes (it previously documented a removed
TableContainerAPI).
1.6.0 — 2026-06-19
Added
clearPersistedState(stateKey, persist?)— forget a grid's saved view (for a "reset saved view" button); pair it with a remount/reload to start frominitialState. SSR-safe.
Docs
- Live dark-mode and persistence demos. The
1.5.0
cssVariablesdark-mode and persistence features were verified end-to-end (grid follows the scheme toggle; state survives a reload).
1.5.0 — 2026-06-17
Integration-quality improvements for teams building on top of <DataTable>.
Added
- State persistence. Set
stateKey="…"to remember pagination, sort, search, filters, and column layout (order / width / visibility / pinning) + density across reloads and remounts — no extra wiring. Tune withpersist?: { storage?: 'local' | 'session' | StorageLike; include?: (keyof TableState)[]; debounceMs?: number }. Selection + expansion are excluded by default; SSR-safe. defaultDensity. An uncontrolled initial density (also honorsinitialState.density) so you can set a starting density without disabling the density selector.densityremains the controlled override.- Type re-exports. The TanStack types used across the public surface —
ColumnPinningState,SortingState,ColumnOrderState,VisibilityState,ColumnSizingState,PaginationState,ExpandedState,RowSelectionState,Row,ColumnDef,Updater— are now exported from the package root, so callbacks/state type without deep imports.
Fixed
- Density selector no longer dead when a default is set. Passing
densitypreviously pinned it (the selector became a no-op). UsedefaultDensity/initialState.densityfor a changeable start. - Dark mode under MUI
cssVariables. The grid now reads colours viatheme.varsand emits its mode-dependent defaults withapplyStyles('dark'), so it followscolorSchemestoggling.createDataTableTheme()emits per-schemecolorSchemesoverrides (with an optionaldarkPalette) instead of a flat top-levelpalette, composing cleanly with acssVariablesapp theme.
1.4.3 — 2026-06-16
Fixed
- Pagination footer survives CSS-heavy host pages. Completes the 1.4.2 fix. Global
p { margin }from host typography (Docusaurus, WordPress themes, Tailwind/Bootstrap resets) was leaking into MUI's pagination<p>labels and ballooning the footer to ~3× height (and 1.4.2'sflex-wrapthen made it wrap). The labels are now pinned flush (margin: 0) and the pagination root spans the full width, so the footer stays a clean single row wherever the grid is embedded — not just in a pristine MUI app.
1.4.2 — 2026-06-16
Fixed
- Pagination footer layout. Tamed MUI's
TablePaginationinside the grid: its padding now aligns with the cells (was cramped against the right edge), it no longer renders a private horizontal scrollbar (overflow: auto), and it wraps gracefully on narrow widths instead of overflowing the rounded card.
1.4.1 — 2026-06-16
Fixed
- Pinned columns now keep column order. Pinning a column right no longer appends it past the others — each side renders in the columns' order, so e.g. a last "Actions" column stays rightmost when you pin another column right.
- Default-pinned special columns survive a custom
initialState. The automatic left-pin of the selection / expander columns now merges withinitialState.columnPinninginstead of being replaced, so pinning your own columns (e.g.right: ['actions']) no longer un-pins the checkbox/expander. Override by pinning a special column yourself.
1.4.0 — 2026-06-16
A design-reference visual refresh, a full column manager, and a customizable toolbar.
Added
renderToolbarrender-prop — arrange the built-in toolbar controls (search, filter, columns, density, export, refresh, reset,extraFilter) in any order or position.slots.toolbaris now also wired for a complete replacement. See Toolbar → Reorder / reposition.- Rebuilt Columns panel (the toolbar ⊞ button): wider + compact, with a per-column show/hide eye toggle, pin left/right, drag-to-reorder, and a Show all / Reset / Done footer. Replaces the old checkbox menu. See The Columns panel.
- Feather-style default icons for the toolbar and sort indicators — lightweight inline SVG (no
@mui/icons-materialbarrel pulled in), still fully overridable throughslots.
Changed
- Default look refreshed to match the design reference: header uses
text.secondarylabels at 13px with subtle column separators. DataTableSlotsreorganized — kept flat (MUI-consistent) but grouped by section, and the permissive[slot: string]index signature was removed so you get real autocomplete + typo-safety.slotPropsnow explicitly typesselectionColumn/expandColumn. (If you were passing an unknown slot key, TS will now flag it.)
Docs
- Confirmed light + dark theme support end-to-end, including under
CssVarsProvider— every colour derives from your MUI theme and follows a light↔dark switch automatically. - New: toolbar reorder demo, Columns-panel guide, and a grouped Slots reference.
1.3.3 — 2026-06-15
Column pinning gets a UI, and row expansion now actually works.
Added
- Column pinning controls. The toolbar's Columns menu now has per-column pin-left / pin-right toggles (next to the visibility checkbox). Click an active side to unpin.
- Default-pinned special columns. When
enableColumnPinningis on, the selection (_selection) and expander (_expanding) columns pin to the left automatically so they stay visible while scrolling. Override viainitialState.columnPinning. - New docs: Column pinning and Row expansion.
Fixed
- Row expansion now renders.
renderDetailPanelwas previously declared but never drawn (the expand chevron toggled state but showed nothing). The grid now registers the expanded row model, renders the detail panel beneath an expanded row, and defaults every row to expandable (gate withgetRowCanExpand).
1.3.2 — 2026-06-15
A modern default look for the grid and toolbar — all still fully overridable via the same
theme/token/slotProps layers.
Added
- Self-framing card. The grid now renders inside a rounded, bordered surface (it finally consumes
the
--dt-radiustoken, floored at 8px). No need to wrap it in your own<Paper>. - Collapsible search. The global search is now a search icon that expands into a field on click (auto-focus, clear button, auto-collapses when emptied) instead of an always-on input.
- Themeable
Toolbarslot (components.MuiTanstackDataGrid.styleOverrides.toolbar). - Selected rows now get a background tint (
--dt-row-bg-selected); rows transition smoothly on hover.
Changed
- Full-width by default.
fitToScreennow defaults totrue, so columns stretch to fill the container (they still never shrink below their defined size — wide tables scroll). PassfitToScreen={false}for the previous fixed-width behaviour. - Modernized dropdowns. Density, columns and export menus get rounded/elevated surfaces, section labels, and the density menu shows a per-option icon + a check on the active density.
extraFiltermoved to the right side of the toolbar (was on the left).- Default header is crisper:
text.primarylabels on a lightergrey[50](light) background; the striped path now uses the dedicated--dt-row-bg-stripetoken.
Docs
- Updated Theming defaults table and Toolbar (collapsible search,
extraFilterplacement, the newtoolbarstyle slot).
1.3.1 — 2026-06-15
Documentation/metadata corrections only — no API or runtime changes.
Fixed
- Corrected a stale banner comment in the published type declarations (
dist/types/index.d.ts) that still described the package as a "Phase 1 scaffold" with the grid components "landing in later phases." It now accurately reflects the shipped surface (and the correct package name).
Docs
- Migration guide: documented the v1 → v2 imperative-handle change — the
forwardRefrefis replaced by anapiRefprop (the API itself is unchanged). Clarified thatstateKey/defaultHiddenColumnswere example-wrapper props, never grid props.
1.3.0 — 2026-06-14
Dual-format build so bundlers tree-shake unused grid code (not just icons).
Added
- Dual ESM + CommonJS output.
importresolves to the ESM build,require()to the CommonJS build, via the packageexportsmap. Works in Vite, webpack, Rollup, esbuild, Next.js, and plain Node. "sideEffects": false— bundlers drop any feature you don't import (importing a single helper now pulls ~200 bytes instead of the whole grid).- TypeScript declarations shipped under
dist/types.
Changed
- Build split into separate CJS / ESM / types passes; ESM relative imports are emitted fully-specified
(
./x.js) so strict resolvers (webpack, Rspack, Next.js, Node native ESM) resolve them.
Docs
- New Bundle size & module formats section in Getting Started; build + module notes added to the package README.
1.2.0 — 2026-06-14
Smaller bundles and customizable icons.
Fixed
- Icons are imported per-path (
@mui/icons-material/Search) instead of from the barrel. Consumers no longer bundle the full ~2,100-icon set — no alias or shim needed. (A build test now guards against this regressing.)
Added
- Per-icon
slotsso you can supply your own icon set (lucide, custom SVGs, a different MUI variant):searchIcon,filterIcon,addFilterIcon,clearIcon,columnsIcon,densityIcon,exportIcon,refreshIcon,resetIcon,expandIcon,collapseIcon, plus the now-wiredsortIconAsc/sortIconDesc. Each defaults to the matching MUI icon. - Sort indicators are sized via CSS, so a custom non-MUI icon renders without React unknown-prop warnings.
Docs
- New Custom icons section in Toolbar.
1.1.0 — 2026-06-13
First public release on npm. Large-data export overhaul.
Added
- Four export modes —
client(default),server-data,server-file, andserver-async— sharing oneExportRequestdescriptor that carries the chosen columns, scope (all / filtered / selected), selection, filters, and sorting. - Streaming export (File System Access API with a blob fallback), line-by-line CSV, lazy-loaded XLSX
with the Excel row cap, native
<a download>for file URLs, CSV-injection sanitizing, and cancellation.
Docs
- Rewrote Export and Server-side docs: both server data approaches (
onFetchDatavs controlleddata+rowCount+onDataStateChange) and the exact filter payload shape for mapping to your API.
1.0.1 — 2026-06-13
- Republish of
1.0.0during npm publishing setup — no functional changes.
1.0.0 — 2026-06-13
Initial release of the rebuilt grid — successor to the now-deprecated
@ackplus/react-tanstack-data-table.
Added
<div>/ CSS-Grid rendering (no HTML<table>) for reliable column sizing, resize, and pinning.- Themeable like a MUI component — inherits your MUI theme; override via
components.MuiTanstackDataGrid,--dt-*CSS variables, orsx. - Sorting, global + column filtering, pagination, row selection + bulk actions, column pinning / reordering / resizing, row expansion, and virtualization.
- CSV + lazy Excel export.
- Server-side data support, a
useDataTable()headless hook, andslots/slotPropsfor overriding any part.