apex-grid 4.0.0 and apex-grid-enterprise 0.9.0 shipped on November 9, 2026, now under the latest npm tag. It is a major version because a few defaults moved. Most apps upgrade with no code change, but check the table below first.

Key takeaways

  • Body rows are recycled by default, so long jumps and sorts cost less on large grids.
  • The grid role moved to [part~="table"], so the toolbar, paginator and filter panel are no longer read as table content.
  • Ignite UI theming is opt-in, and the grid follows the family --apx-* tokens.
  • New: showHeader, selection.isRowSelectable, a compact paginator, ctx.data in templates, and IME input.

What can break when I upgrade?

ChangeWhat you will seeHow to keep the old behaviour
Body rows are recycledA template's element state (an input's typed value, a widget), a held grid.rows element, or ctx.row read after an await shows another record after a scroll, sort or filterrowRecycling = false or row-recycling="false"; or read ctx.data and wrap stateful templates in keyed(ctx.data, ...)
Grid role on [part~="table"]role, aria-rowcount, aria-colcount, aria-multiselectable and the accessible name are gone from the hostQuery grid.shadowRoot.querySelector('[part~="table"]'). A host aria-label still names the grid
Ignite UI theme is opt-inAn Ignite UI theme no longer re-tints the brand or replaces the fontimport 'apex-grid/ignite-ui'
--ag-font has no root defaultPage CSS reading var(--ag-font) gets nothingGive it a fallback, as with --ag-brand. The default Inter stack is unchanged
The grid follows --apx-* tokens--apx-accent or --apx-surface set on :root for charts restyles the grid tooSet the matching --ag-* tokens, or set the family tokens on the elements they are meant for instead of :root
Pinned rows leave the page counttotalItems, the pages and the paginator summary count only the rows the pager pages throughNone
Local page changes skip the pipelineCustom sort and filter hooks in dataPipelineConfiguration are not called on a local page changeNone; remote mode still reruns them
Pagination types removedCode setting DataPipelineConfiguration.pagination or the 'pagination' value of DataPipelineParams.type stops compilingRemove it: the hook was never called. Use pagination.mode: 'remote' for server paging
min() and max() fail text'abc' fails both validatorsNone; empty values still pass
Smaller type and DOM changesctx.data is required on BaseApexCellContext, pageSizeOptions is readonly number[], the paginator's previous button id is prev (was previous), protected headerRowCount and renderDragGhost are goneSupply ctx.data in hand-built contexts; target apex-grid::part(paginator-prev)

The v3 to v4 migration guide covers each row in more detail.

Why are rows recycled now?

A scroll, sort or filter re-binds the rendered row elements to the records now in view instead of building new rows. Templates should render from their context every time and read the record from ctx.data, which stays that record after an await while ctx.row can be re-bound:

import { html } from 'lit';
import { keyed } from 'lit/directives/keyed.js';

{
  key: 'widget',
  cellTemplate: (ctx) => keyed(ctx.data, html`<my-widget .value=${ctx.value}></my-widget>`),
}

Detail templates need no change. See Row recycling.

What is new for compact grids?

  • showHeader (show-header="false") leaves out the header rows and the filter panel; screen readers still announce the column names. See Hiding the header.
  • selection.isRowSelectable(row) keeps rows such as a pinned total out of selection: no checkbox, and clicks, Select all and selectRow skip them. See Row Selection.
  • pagination.paginator: 'compact' draws a small paginator, and apex-grid::part() reaches every paginator and toolbar part. See Compact paginator.

How does Ignite UI theming work in 4.0?

Import the add-on once and every grid on the page follows the Ignite UI theme the page loads: brand, text, gridlines, header, state colours, font, the enterprise popups and the light or dark variant, live.

import 'apex-grid/define';
import 'apex-grid/ignite-ui';

In Blazor, call ApexGridIgniteUI.Enable() in Program.cs instead. An --ag-* token you set still wins, then the family --apx-* tokens, then Ignite UI, then the built-in values. Per grid, design-system="off" turns the Ignite UI colours and font off and tokens="off" turns off the family tokens too. The add-on is about 2.5 KB gzip and needs no extra licence key. See Using with Ignite UI and Family tokens.

What changed for input methods and typed numbers?

Enter, Escape and Tab that confirm or cancel an IME conversion are left to the IME, so they no longer commit a cell edit, close the filter panel or clear the quick filter. A number typed in the filter panel, or checked by min() and max(), reads full-width digits (123), the column's locale grouping (1.234,5 in de-DE) and a currency mark. See Typed numbers and input methods.

Text that stayed English now has locale keys: the CSV export item, the filter panel's and / or toggles, rating and boolean accessible names, and the built-in validator messages (a custom validator reads them through ValidatorContext.localize). See Localization.

What changed in Enterprise 0.9?

Enterprise 0.9.0 needs apex-grid 4.0.0; its modules fail to load against a 3.x grid. Coming from 0.8.0, apps may notice:

  • Rows are recycled in <apex-grid-enterprise> too; master-detail grids pass rowRecycling on to detail grids.
  • A bulk write (applyEdits, a range paste, a fill drag, undo or redo of a batch) fires one apex-view-changed.
  • Only visible columns reach a model or proxy, the proxy body adds rowCount, and a proxy reply whose answer is not a string or patch not an object rejects. See AI Toolkit.
  • Charts skip blank cells and format numbers for the locale.

New in 0.9: the popups and chart panel follow the grid's theme, IME input works in the formula editor, chart dialog, AI prompt and search fields, =SUM(A1:A3) is stored as =SUM(A1:A3), pasting ¥1,200 into a numeric column stores 1200, and the filter builder and header menu text moves into locale keys.

See Enterprise 0.9.

React and Blazor

react-apex-grid 0.1.1 accepts apex-grid ^3.4.0 || ^4.0.0, and rowRecycling, designSystem and tokens pass through as props. Blazor-ApexGrid 1.2.0 bundles apex-grid 4.0.0 and adds RowRecycling (RowRecycling="false" keeps record-keyed rows), ApexGridIgniteUI.Enable(), and the DesignSystem and Tokens parameters. See Using with Ignite UI.

Upgrading

npm install apex-grid@4.0.0
# with the enterprise package
npm install apex-grid@4.0.0 apex-grid-enterprise@0.9.0
# with the React wrapper
npm install apex-grid@4.0.0 react-apex-grid@0.1.1

For Blazor, dotnet add package Blazor-ApexGrid --version 1.2.0. Then check:

  • Templates that keep element state, and code that holds grid.rows or ctx.row.
  • Tests that read role or aria-rowcount from the host.
  • Pages with an Ignite UI theme: add import 'apex-grid/ignite-ui', or in Blazor call ApexGridIgniteUI.Enable() in Program.cs.
  • TypeScript code that sets DataPipelineConfiguration.pagination.
  • The rest of the table above: --apx-* tokens on :root, var(--ag-font), pinned rows and pipeline hooks with pagination, min()/max() on text, and #previous selectors.

The full lists are in the core and enterprise CHANGELOG files in the apex-grid repository.

Previous release: ApexGrid 3.6 and Enterprise 0.8.

Frequently asked questions

Why does a row show another record after upgrading to ApexGrid 4.0?

From 4.0 body rows are recycled by default: a scroll, sort or filter re-binds the rendered row elements to the records now in view. A cell template that keeps state in the elements it creates, or code that holds grid.rows elements or ctx.row across an await, can see another record. Read ctx.data in templates, wrap a stateful template in keyed(ctx.data, ...), or set rowRecycling = false (row-recycling="false") to keep record-keyed rows.

Why did my Ignite UI theme stop applying to ApexGrid 4.0?

In 3.x the grid took the brand colour and font from an Ignite UI theme on the page by itself. In 4.0 an Ignite UI stylesheet changes nothing until you import 'apex-grid/ignite-ui' once (in Blazor, call ApexGridIgniteUI.Enable() in Program.cs). The add-on brings the brand and font back along with text, gridlines, header, state colours and the enterprise popups.

Why do my tests no longer find role="grid" on the apex-grid element?

The grid role, aria-rowcount, aria-colcount, aria-multiselectable and the accessible name moved from the host to the inner element exposed as part table. Query grid.shadowRoot.querySelector('[part~="table"]') instead. An aria-label or aria-labelledby on the host still names the grid.