Skip to main content

Grid API

IGridAPI is available from React's ref/onGridReady, Angular's gridReady output or template reference, and createGrid() in a core integration.

Reconfiguring a mounted grid

MethodPurpose
updateGridOptions(options)Change presentation and behavior options in place, preserving scroll, selection, focus, and edit history
registerMenuAdapter(adapter)Install (or remove, with null) the column-menu adapter on a mounted grid
registerBodyMenuAdapter(adapter)The same for the body context menu

Only the properties present in options change; a property present with the value undefined resets to the grid's default, which is how a callback such as getRowStyle is removed. Object values replace wholesale — they are not deep-merged.

api.updateGridOptions({ toolbar: { sorting: true, export: true } });
api.updateGridOptions({ zebraRows: true, rowHover: false });
api.updateGridOptions({ tooltip: { mode: "follow" } });
api.updateGridOptions({ getRowStyle: undefined }); // back to the default

This is what the React and Angular bindings do when a prop changes, so a framework-neutral host reaches the same behavior without recreating the grid. UpdatableGridOptions lists what can change: visual and interaction options, toolbar / quick filter / tooltip / column panel / saved views, pagination and its controls, row selection, theme and icons, pinned-row bands, row-group presentation, the server-side data source, and columnDefs.

Options that seed structure are fixed at creation and are rejected with a warning: rowHeight and the header heights (virtualization geometry), rowNumbers and rowModelType (which columns and row model exist), and getRowId / rowIdKey (row identity). Changing one of those means creating a new grid.

The menu adapters are not options — they are assembly ingredients, like columnDefs, so they have their own registration methods rather than a key in updateGridOptions:

api.registerMenuAdapter(adapter); // effective on the next menu open
api.registerBodyMenuAdapter(null); // remove; back to the built-in body menu

Both are the deferred form of createGrid's menuAdapter / bodyMenuAdapter options, for a host that does not know its adapter when the grid is created. Registration needs no rebuild, and a menu that is already open keeps the items and cleanup it was given. Reach for an adapter only when menu items are rendered by a framework and must be unmounted again — application-owned items belong in ColDef.columnMenu, multiColumnMenu, or bodyContextMenu, all of which run before the adapter.

Data and columns

MethodPurpose
setRowData(rows)Replace client-side rows
forEachNodeAfterFilter(callback)Visit rows that pass filtering, before client-side sorting
forEachNodeAfterFilterAndSort(callback)Visit rows that pass filtering in final sorted order
applyTransaction(tx)Add, optionally insert at source-order addIndex, update, or remove client-side rows; returns { added, updated, removed } counts of what was actually applied (unknown row IDs are skipped and not counted)
applyTransactionAsync(tx)Mutate client-side rows immediately and defer model derivation/rendering into a shared batch; resolves after the flush with this call's counts
flushAsyncTransactions()Immediately finalize pending asynchronous transactions
setColumnDefs(defs)Replace column definitions (claims caller ownership of the schema — see below)
getColumnState()Capture width, order, pinning, and visibility
applyColumnState(state, options?)Merge or exactly restore state
getColumnModel()Read runtime columns and sections
refreshServerSideData(options?)Soft-refresh or purge a server cache/subtree
refreshRowPresentation()Re-evaluate row presentation for rendered body, pinned, and sticky rows after external state changes

Who owns the column schema. A server-side response can carry its own columns, and the grid arbitrates between that and the application: once the application supplies definitions through any door — columnDefs at creation, a wrapper prop, updateGridOptions({ columnDefs }), or setColumnDefs — server-sent schemas are ignored. For a non-empty array, setColumnDefs(defs) and updateGridOptions({ columnDefs: defs }) are equivalent; they differ only at the edges: setColumnDefs([]) clears the columns and keeps ownership, while updateGridOptions({ columnDefs: [] }) clears only if the application already owned the schema (a transient empty array from a framework render must not discard a server schema). To hand the schema back to the server, release ownership with updateGridOptions({ columnDefs: undefined }).

View and grouping

MethodPurpose
setQuickFilter(text, options?)Apply client-side global search
getFilterModel()Read the per-column filters as serializable { colId, filters, join } items
setFilterModel(filters)Replace all column filters (empty array clears; resets to page 1 and clears selection)
addFilterModel(filter)Add or replace one column's filter, keeping the others
removeFilterModel(colId)Remove one column's filter (no-op when it has none)
getSetFilterValues(colId)Set-filter columns: the value universe (null = blanks)
getSetFilterState(colId)Intent-level { mode, checked, unchecked }, or null when unfiltered
checkSetFilterValue(colId, value) / uncheckSetFilterValue(colId, value)Toggle one value by intent
setSetFilterValues(colId, values, { mode }?)Replace the set filter; mode pins include/exclude semantics
setAllGroupsExpanded(expanded)Expand/collapse all groups in one pass
getGroupNodes()Every group / tree node in the model (empty when not grouped); forEachNodeAfterFilter* walk data rows only
setRowGroupColumns(colIds) / getRowGroupColumns()Replace/read the row-grouping columns by colId (level order; empty array clears)
setAggregates(aggregates) / getAggregates()Replace/read the aggregate assignments as { colId, type } entries (footer totals, group totals, pivot measures; entry order sets generated pivot column order)
setPivotMode(on) / getPivotMode()Toggle pivot mode (client-side; off restores the pre-pivot state exactly, on reinstates the last pivot)
setPivotColumns(colIds) / getPivotColumns()Replace/read the pivot columns (level order; stored even while mode is off)
getPivotResultColumns()Descriptors of the generated pivot value columns, in header order
setPivotColumnOrder(order) / getPivotColumnOrder()Replace/read the manual arrangement of the generated pivot columns (leaf order by generated colId; null = canonical). Role edits reset it
captureViewState()Capture columns, groups, sort, filters, search, expansion, page, and the inactive pivot state layer
applyViewState(state, options?)Restore a captured view
setKeyboardNavigationMode(mode)Switch tree grid/hierarchy navigation imperatively (source: "api")
setTreeDataKeyboardNavigationOptions(options)Reconfigure the mode and/or the Ctrl/Cmd+Shift+Space switch (source: "options")

Filter, grouping, aggregate, and pivot methods address columns by the public ColDef.colId. Explicit sort models use typed dispatch() actions.

Selection and navigation

setFocusedCell, selectRange, extendRangeTo, selectRow, selectColumn, selectAll, selectAllRows, clearSelection, and navigate mirror rendered keyboard and pointer behavior. selectRowsById(ids, mode?) drives row selection programmatically by stable row id ("set" | "add" | "remove"). getSelection, getSelectedRows, and getSelectedNodes return current state (always copies — safe to mutate). selectAllRows/areAllRowsSelected span the whole filtered set by default (selectAllScope option).

Keyboard shortcuts

MethodPurpose
registerShortcut(shortcut)Register an application keyboard shortcut on this grid instance; returns the disposer
getKeyboardShortcuts()The whole binding table — built-ins and application shortcuts — as data for a shortcut reference
const off = api.registerShortcut({
id: "approve",
chord: "mod+shift+y", // mod = Ctrl on Windows/Linux, Cmd on macOS
label: "Approve the selected rows",
run: () => approveRows(api.getSelection()),
});

Application shortcuts resolve after every built-in (override: true registers ahead of the non-blocking built-in scopes instead), and reserved chords are refused with an error naming the feature that owns them: Tab and Escape always; arrows, Home/End, Enter, Space, and PageUp/PageDown only while the surface that claims them is on (cellSelection, headerKeyboardNavigation) — a display-only grid frees them all. mod+alt+<printable> is refused outright (Windows AltGr reports as Ctrl+Alt). Format a chord for the user with the exported formatChord(chord); menu items show accelerators via MenuItem.shortcut (a display hint) or automatically when a built-in binding shares the item's command.

Scrolling to a row, column, or cell

MethodPurpose
ensureRowVisible(rowId, { position }?)Scroll a row into view by stable row id
ensureColumnVisible(colId)Scroll a column into view horizontally
ensureCellVisible(cell, { position }?)Both axes at once

These are for "jump to row" flows — a validation error, a find-in-grid result, a row a notification just arrived about — and they do whatever it takes to get the row on screen first: collapsed group/tree ancestors are expanded (batched into one repaint), and under pagination the grid pages to the row, firing paginationChanged once. Rows are addressed by stable row id and columns by colId, so you never compute a page or a view index yourself.

position says where the row should land: "auto" (default) scrolls the minimum needed and leaves an already-visible row alone; "top", "middle", and "bottom" place it deliberately even when it is already on screen — a jump the user asked for is easier to follow when the row moves somewhere predictable. "top" means the top of the usable viewport, below any sticky group headers docked there.

Each method returns whether the target is visible afterwards. false means no amount of scrolling would have shown it:

  • an unknown row id or colId;
  • a row the active filter excludes, or a column that is hidden (including one hidden by a collapsed column group — these do not expand column groups);
  • on the server-side row model, a row that is not loaded. The server owns the row order, so the grid cannot work out which page an unloaded row is on; page or filter to it instead, then scroll.

A row mirrored into a frozen top/bottom band is already on screen, so revealing it succeeds without moving the body (the band itself scrolls if it is taller than the space it has). Both halves of ensureCellVisible are attempted independently — a bad colId still gets you to the row — but it returns true only when both succeeded.

if (!api.ensureCellVisible({ rowId: error.rowId, colId: error.field }, { position: "middle" })) {
// The row is filtered out or unloaded — clear the filter (or page to it) and try again.
}

Editing and clipboard

MethodPurpose
startEditingCell(cell)Begin editing { rowId, colId }
stopEditing(value) / cancelEditing()Finish or cancel the active edit
setCellValue(cell, value)Write one cell without opening an editor
setCellValues(edits)Write many cells as one undo step (changed cells only)
copySelection() / cutSelection() / paste()Clipboard workflows
undo() / redo()Traverse edit history
canUndo() / canRedo() / clearHistory()Inspect or reset history
getHistoryState(){ canUndo, canRedo, undoDepth, redoDepth }
withUndoGroup(fn)Coalesce the writes in fn into one undo step
withoutUndoHistory(fn)Apply the writes in fn without recording them

A programmatic write states its own type: a string value runs through the column's valueParser (like typed input), anything else is stored as the final value. So setCellValue(cell, 99) stores the number 99 even with no parser configured, while setCellValue(cell, "99") gives the parser its say.

Floating UI and pinned rows

showTooltip, hideTooltip, openActionFrame, and closeActionFrame address cells by stable row and column IDs. setPinnedTopRowData, setPinnedBottomRowData, and setRowPinned update frozen row bands.

Rows supplied through pinnedTopRowData or pinnedBottomRowData stay fixed in their band: filtering, sorting, and pagination apply only to body rows. They are also outside row selection, so selectAllRows(), selectRowsById(), and the selected-row APIs do not include them. Pinned cells can still be focused, edited, and included in cell-range or column selections.

Export

exportDataAsCsv and exportDataAsExcel download files. getDataAsCsv and getDataAsExcel return content for custom upload, encryption, or storage.

Events and cleanup

Subscribe with api.on(eventName, handler) and call the returned unsubscribe function. destroy() releases the grid when a custom host owns its lifecycle.

filterChanged is the canonical "effective filter changed" signal: it fires once for every column-filter model change (API, filter menu, dispatch), every quick-filter change (source: "quickFilter", client-side row model only), and any columnDefs update that drops an active filter (source: "columns") — no need to subscribe to both columnsChanged {reason:"filter"} and modelUpdated {reason:"filter"} (both still fire for back-compat). On the client-side row model the handler runs after the view is re-derived, so row counts and getPaginationInfo() read post-filter state. The declarative onFilterChanged option callback wraps it.

historyChanged fires whenever the undo/redo stacks move, with { reason, canUndo, canRedo, undoDepth, redoDepth } — enough to drive toolbar button state without polling. It follows the write's own cellValueChanged / cellsChanged events, so a handler reading getHistoryState() sees the value and the history agree. Writes that never enter history — a vetoed commit, a commit that doesn't change the stored value, anything under readOnlyEdit, applyTransaction, or a write inside withoutUndoHistory — do not fire it, and a whole withUndoGroup scope fires once on exit. The declarative option callback is onHistoryChanged.

Column identity in payloads and inputs

Every colId field carried by event payloads (cellsChanged, cellValueChanged, editingChanged, columnsChanged, columnWidthsChanged, cellClicked, filterChanged, tooltip and focus events, onCellValueChanged, onSortChanged, selection rangeCells, …) holds the public ColDef.colId — the id you defined. The column's internal per-instance UUID rides alongside in a colInstanceId / colInstanceIds / changedColInstanceIds field; you only need it to disambiguate the rare case of split/moved column duplicates that share a colId, or to key caches that must survive such splits.

Every column id the grid accepts (API methods, dispatched actions, CellRef inputs) is resolved tolerantly — instance id, public colId, or key all work — so api.startEditingCell({ rowId: "1", colId: "price" }) just works.