Skip to main content

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.x releases are backward compatible. Install a specific version with npm 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 a select column 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); processRowUpdate fires once per row with the fully-updated row. Start via double-click / Enter / apiRef.current.editing.startRowEdit(id); onRowEditStart / onRowEditStop callbacks; the actions are also exported as createRowEditAction to mix with your own getRowActions.
  • Custom cell editors. columnDef.editComponent now actually drives the inline editor (it was previously dead). It receives DataTableEditComponentProps (value / onChange / onCommit / onCancel / row / column / align / editMode) and works in both cell and row mode.

Changed

  • columnDef.editComponent is no longer a fallback for the column filter input — use filterComponent for that. (No known consumers relied on the fallback.)

Docs

  • Editing documents row mode, the editComponent editor, and apiRef.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. New setNestedValue util (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 imperative apiRef.current.data API, 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 sets dir="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, and aria-rowindex/aria-rowcount/aria-colspan account for the extra header rows. See Columns → Grouped headers.

Docs

  • New Grouped headers (Columns) and RTL (Theming) sections + live demos.

1.15.0 — 2026-06-21

Third remaining backlog slice. Backward compatible.

Added

  • Saved / named views. enableSavedViews adds 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 under dt:<stateKey>:views (uncontrolled) or driven via views / onViewsChange / activeViewId / onActiveViewChange (controlled). Full apiRef.current.views namespace (saveView / applyView / updateView / renameView / deleteView / resetView / listViews / getActiveView / isDirty) and a viewsIcon slot.

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


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 with initialState.rowPinning; pin via apiRef.current.rowPinning (pinRowTop / pinRowBottom / unpinRow / setRowPinning / resetRowPinning / getPinnedRowIds) or the exported createRowPinAction helper (spread into getRowActions). Fires onRowPinningChange; included in saveLayout/restoreLayout. Client data mode only (ignored under server pagination); top-level rows only (not tree sub-rows). Persist with persist.include: ['rowPinning'] plus a stable getRowId.

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 (needs enableColumnVisibility). 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: true per column; swap the glyph with slots.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. rowsPerPageOptions sets the footer's page-size choices (default [5, 10, 25, 50, 100]; [] hides the selector). Replace the whole footer with slots.pagination.
  • Faceted (auto) filter options. A type: 'select' column with no options auto-populates its filter values from the data's distinct values (cardinality-guarded). Values reflect the data, not other active filters.
  • Clipboard copy. enableClipboardCopy adds a Copy action to the bulk-actions bar (tab-separated text that pastes into Sheets/Excel); also apiRef.current.clipboard.copySelectedRows() and the onClipboardCopy(count) callback.

Docs


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, the rowCount table 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-live region announces sort, filter, page, and selection changes (localizable: announceSort, announceFilteredRows, announcePage, selectedRows).
  • Persistable tree expansion. Add 'expanded' to persist.include to 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" (treegrid for tree data), aria-rowindex/aria-colindex/aria-rowcount/aria-colcount, header aria-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 for select/boolean); Enter or blur commits, Escape cancels. processRowUpdate(newRow, oldRow) persists the change (reverts + onProcessRowUpdateError on a throw); without it, edits update the grid's local data.
  • Tree data. getSubRows renders hierarchical rows — an expander only on rows with children, depth indentation, and role="treegrid" + aria-level / aria-expanded. Use instead of renderDetailPanel.

Changed

  • The scroll container is now role="grid" (was role="table") and body cells role="gridcell", per the ARIA grid pattern.

Docs

Finishes Tier B — a footer aggregation row and full localization. Backward compatible.

Added

  • Footer aggregation. enableAggregation + a per-column aggregation ('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 via apiRef.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 partial localeText (unset keys fall back to English). Operator labels localize while the underlying operator value (the logic key) is unchanged. The English bundle is exported as enUS / DEFAULT_LOCALE_TEXT; interpolated strings (selectedRows, paginationDisplayedRows) are functions.

Docs


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 _actions column (pinned right when enableColumnPinning is on). A few actions render as inline icon buttons; 3+ (or any without an icon) collapse into an overflow menu. Each action is { label, icon?, onClick(row), disabled?, hidden?, color? }. Configure via slotProps.actionsColumn, rowActionsDisplay ('icons' | 'menu' | 'auto'), and slots.moreActionsIcon. Handlers stop propagation so they don't trigger onRowClick.
  • column.valueGetter — derive a value for a column with no accessorKey; it compiles to a TanStack accessorFn, 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 matching options label — when you don't supply a cell. 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.cellClassNamestring or ({ value, row }) => string applied to each body cell of the column (DataGrid-style conditional styling).
  • column.headerClassNamestring applied 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-column cellClassName.
  • between filter 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 / autoSizeAllColumns now 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's minSize/maxSize and 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 wrapText and the class-name hooks, and a note on double-click auto-fit. The Filtering page documents the between range 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

  • stickyFooter now works. It was declared but never wired; it now pins the footer and bounds the body like stickyHeader.
  • maxHeight is active on its own. Previously it was ignored unless stickyHeader (or enableVirtualization) was also set; setting maxHeight now caps and scrolls by itself.
  • enableStickyHeaderOrFooter (the deprecated alias) is honored again — it maps to stickyHeader.
  • 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 TableContainer API).

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 from initialState. SSR-safe.

Docs

  • Live dark-mode and persistence demos. The 1.5.0 cssVariables dark-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 with persist?: { storage?: 'local' | 'session' | StorageLike; include?: (keyof TableState)[]; debounceMs?: number }. Selection + expansion are excluded by default; SSR-safe.
  • defaultDensity. An uncontrolled initial density (also honors initialState.density) so you can set a starting density without disabling the density selector. density remains 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 density previously pinned it (the selector became a no-op). Use defaultDensity / initialState.density for a changeable start.
  • Dark mode under MUI cssVariables. The grid now reads colours via theme.vars and emits its mode-dependent defaults with applyStyles('dark'), so it follows colorSchemes toggling. createDataTableTheme() emits per-scheme colorSchemes overrides (with an optional darkPalette) instead of a flat top-level palette, composing cleanly with a cssVariables app 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's flex-wrap then 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 TablePagination inside 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 with initialState.columnPinning instead 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

  • renderToolbar render-prop — arrange the built-in toolbar controls (search, filter, columns, density, export, refresh, reset, extraFilter) in any order or position. slots.toolbar is 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-material barrel pulled in), still fully overridable through slots.

Changed

  • Default look refreshed to match the design reference: header uses text.secondary labels at 13px with subtle column separators.
  • DataTableSlots reorganized — kept flat (MUI-consistent) but grouped by section, and the permissive [slot: string] index signature was removed so you get real autocomplete + typo-safety. slotProps now explicitly types selectionColumn / 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 enableColumnPinning is on, the selection (_selection) and expander (_expanding) columns pin to the left automatically so they stay visible while scrolling. Override via initialState.columnPinning.
  • New docs: Column pinning and Row expansion.

Fixed

  • Row expansion now renders. renderDetailPanel was 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 with getRowCanExpand).

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-radius token, 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 Toolbar slot (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. fitToScreen now defaults to true, so columns stretch to fill the container (they still never shrink below their defined size — wide tables scroll). Pass fitToScreen={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.
  • extraFilter moved to the right side of the toolbar (was on the left).
  • Default header is crisper: text.primary labels on a lighter grey[50] (light) background; the striped path now uses the dedicated --dt-row-bg-stripe token.

Docs

  • Updated Theming defaults table and Toolbar (collapsible search, extraFilter placement, the new toolbar style 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 forwardRef ref is replaced by an apiRef prop (the API itself is unchanged). Clarified that stateKey / defaultHiddenColumns were 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. import resolves to the ESM build, require() to the CommonJS build, via the package exports map. 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 slots so 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-wired sortIconAsc / 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


1.1.0 — 2026-06-13

First public release on npm. Large-data export overhaul.

Added

  • Four export modesclient (default), server-data, server-file, and server-async — sharing one ExportRequest descriptor 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 (onFetchData vs controlled data + rowCount + onDataStateChange) and the exact filter payload shape for mapping to your API.

1.0.1 — 2026-06-13

  • Republish of 1.0.0 during 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, or sx.
  • 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, and slots / slotProps for overriding any part.