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
getGridOption(key)Read one option: the value last supplied (to updateGridOptions or a wrapper prop, else at creation), as given; undefined for an option never set — no defaults are filled in. pivotMode and pivotColumns read the live pivot state
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, the header heights, 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 (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 rows; returns { added, updated, removed } counts of what was actually applied (unknown row IDs are skipped and not counted). Server-side, it mirrors a change already made on the server into the loaded rows, with additions placed by route (see Transactions)
applyTransactionAsync(tx)Mutate 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, visibility, and sort (sort / sortIndex, null when unsorted)
applyColumnState(state, options?)Merge or exactly restore state; entries with a sort field set (or, with null, clear) that column's sort
autoSizeColumns(colIds) / autoSizeAllColumns()Fit columns to their content, dropping user widths, with one columnWidthsChanged for the batch
setColumnWidth(colId, widthPx) / setColumnWidths(widths)Set widths as a resize drag would (clamped to the column's bounds, kept across autosizes); one event per call
sizeColumnsToFit()Scale the visible unpinned columns to fill the centre viewport exactly (pinned, resizable: false, and utility columns keep their widths)
setColumnPinned(colIds, pinned)Pin one column (a colId) or several to "left" / "right", or unpin with null — the column menu's Pin items; unpinnable columns are skipped; columnsChanged with reason: "pin"
setColumnsVisible(colIds, visible) / setColumnVisible(colId, visible)Show or hide columns; hiding skips hideable: false columns; columnsChanged with reason: "visibility", none when nothing changes
moveColumn(colId, toIndex) / moveColumns(colIds, toIndex)Move columns (as a block, in the order given) to toIndex of getColumnState() order — every non-internal leaf, hidden included, left → centre → right. Each column stays in its section (the index is clamped to it); lockPosition and movable: false columns are skipped, locked columns keep their edge; a group's colId moves the group. One columnsChanged with reason: "order", none when nothing moved
getColumnGroupState()Read every column group's expander state (open/closed) by colId, nested groups included
setColumnGroupState(groups)Open or close column groups by colId in one rebuild — what the header expander does; groups not named keep their state
getColumnModel()Read runtime columns and sections
getColumnDefs()The definitions as the grid holds them now, with the current width, pinning, visibility, sort, aggregate, and group open state written in — setColumnDefs(getColumnDefs()) changes nothing (see below)
getColumn(colId) / getColumns()One application column (tolerant id lookup), or every leaf in display order with hidden ones — the columns getColumnState() describes. Internal columns (row numbers, selection checkbox, auto-group, pivot results) are left out
addSparklineColumn({ colIds, sparklineType, targetColId?, colId? })Add a transient sparkline column over colIds — the column menu's Show Sparklines; returns its colId, or null when no id names a column (Sparklines)
refreshServerSideData(options?)Soft-refresh or purge the server cache, or one subtree of it — groupKeys for a group path, rowId for a server-tree row
refreshRowPresentation()Re-evaluate row presentation for rendered body, pinned, and sticky rows after external state changes
forEachNode(callback)Visit every node the model holds — data rows (filtered-out and collapsed included), then group nodes
forEachLeafNode(callback)Visit every data (non-group) row, filtered-out and collapsed included

Reading the definitions back. getColumnDefs() returns a fresh tree in the current display order and structure, groups with their children. Each entry is the column's own definition as last applied — before defaultColDef is merged in, with the same function and object references — with the current state written over it: colId (always set), a leaf's width, a top-level column's pinned (removed when unpinned), hidden (removed when shown), sort / sortIndex (removed when unsorted), aggFunc for an aggregated column (its first aggregate), and openByDefault for a group with an expander. Row-grouping columns, a column's second and later aggregates, and sorts on the auto-group column have no place in a definition: setColumnDefs keeps them on the same grid (it matches columns by colId), and captureViewState() carries them to another one.

const defs = api.getColumnDefs();
api.setColumnDefs(defs.map(def => def.colId === "price" ? { ...def, label: "Unit price" } : def));

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 (options.behavior chooses filtering or finding)
getQuickFilterText()Current quick-filter text ("" when inactive)
getQuickFilterBehavior()Whether the search filters rows ("filter") or highlights matches ("find")
getFindState()Find behavior: { behavior, available, text, matchCount, activeIndex, activeMatch }
findNext() / findPrevious()Step the active find match in display order (wraps), revealing it — expanding ancestors, paging, scrolling, and moving the search widget aside when nothing else can uncover the cell
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
setGroupExpanded(groupIds, expanded)Expand/collapse the named group / tree nodes in one rebuild; unknown ids are ignored, nothing happens when they are already in that state
getGroupNodes()Every group / tree node in the model (empty when not grouped); forEachNodeAfterFilter* walk data rows only
getRowNode(rowId)The node for a data row, a group / tree node (client-side; loaded rows server-side) or a pinned band row; undefined for an unknown id — a regroup retires the old group ids
getPaginationInfo()Current pagination state, the same object paginationChanged carries: { paginationEnabled, pageIndex, pageSize, totalRowCount, totalPageCount, totalRowCountKnown, pageSizes } (pageIndex is 0-based); counts are over the filtered view, and totalRowCountKnown is false while a server-side total is still provisional
getCellValue(cell)The raw value at { rowId, colId }; the read-side counterpart of setCellValue: a leaf's column value (valueGetter, else the data field), a group row's group cell value (aggregate, label, groupValueGetter); rows resolve like getRowNode; undefined for an unknown row or column
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). type is a built-in AggregateType or a name from GridOptions.aggFuncs; an unknown name warns once and the column's default function stands in. Server-side, any name travels to the server unchanged. setAggregates([]) also clears what ColDef.aggFunc seeded
setPivotMode(on) / getPivotMode()Toggle pivot mode (client-side; off restores the pre-pivot state exactly, on reinstates the last pivot)
isPivotUnconfigured()Pivot mode is on with no row group, pivot column or value — the blank canvas (no columns, no rows)
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, column-group expanders, groups, sort, filters, search (text and match settings), aggregate scope, runtime row pins, 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")

Sort, filter, grouping, aggregate, and pivot methods address columns by the public ColDef.colId.

Sorting​

MethodPurpose
getSortModel()The sort model as { colId, dir } entries in priority order (a copy)
setSortModel(items)Replace the whole sort model ([] clears); unknown and non-sortable columns are skipped
api.setSortModel([{ colId: "region", dir: "asc" }, { colId: "revenue", dir: "desc" }]);

A model equal to the current one does nothing; otherwise setSortModel behaves like a header-click sort (columnsChanged with reason: "sort", onSortChanged, resetPageOn, selectionPersistence). See Sorting.

Pagination​

MethodPurpose
getPaginationInfo()The current state (see View and grouping)
setPagination({ enabled?, pageIndex?, pageSize? })Change any mix of the three, merged over the current state
goToPage(pageIndex)Go to a 0-based page
nextPage() / previousPage() / firstPage() / lastPage()Step through the pages
setPageSize(pageSize)Change the page size and return to the first page
api.setPagination({ enabled: true, pageSize: 50 });
api.nextPage();

Page indexes are clamped to [0, totalPageCount - 1] while the total is known. While a server-side total is still provisional (totalRowCountKnown: false, the "N+" pager) a page past the last loaded one is accepted, and the grid snaps back to the real last page once the count is pinned; lastPage() goes to the last page loaded so far. A new pageSize without a pageIndex returns to page 0, as the footer's selector does; it need not be one of pageSizes (the selector lists only those). Turning pagination off lands on page 0, and the page helpers do nothing while it is off. A call that leaves the state as it is does nothing; otherwise paginationChanged fires once and the body scrolls to the top.

Displayed and rendered rows​

MethodPurpose
getDisplayedRowCount()Rows the current page displays (group rows and expanded leaves; the whole view with pagination off)
getDisplayedRowAtIndex(viewIdx)The node at a page-relative view index, or undefined (server-side, also for a row not loaded yet)
getFirstDisplayedRowIndex() / getLastDisplayedRowIndex()The rendered body window, overscan rows included; -1 when nothing is rendered
getRenderedNodes()The nodes rendered right now: top band, body window, bottom band

These indexes are page-relative — the same space selectRow, setFocusedCell, and the rendered rows use: 0 is the first row of the current page. Add pageIndex * pageSize from getPaginationInfo() for a position in the whole list. Application-pinned band rows are not counted by getDisplayedRowCount().

Repainting​

MethodPurpose
refreshCells({ rowIds?, colIds?, force? }?)Re-run value getters, formatters, cell styles, and renderers of rendered cells
redrawRows({ rowIds? }?)Destroy and rebuild rendered rows and their renderers
flashCells({ rowIds?, colIds?, flashDuration?, fadeDuration? }?)Briefly highlight rendered cells, as the change-flash renderer does

refreshCells is for state a cell reads from outside the row data — a lookup table, a locale, a flag a renderer closes over. Nothing is re-derived (no re-sort or re-filter) and the data is untouched; omitted lists mean every rendered row or column. The grid does no change detection when it repaints, so every targeted cell is re-run: a custom renderer gets new params through its refresh(), and force: true destroys and recreates it instead. Rows that are not rendered are skipped — they paint fresh when they scroll in.

currencyRates = nextRates;
api.refreshCells({ colIds: ["price", "total"] });

api.flashCells({ rowIds: pushedRowIds, flashDuration: 300 });

flashCells uses the change-flash renderer's neutral highlight (--pte-cell-flash-neutral-bg-color, fading over --pte-cell-flash-fade-duration); durations default to the cellFlashDuration and cellFadeDuration options.

Selection and navigation​

setFocusedCell, selectRange, extendRangeTo, selectRow, selectColumn, selectAll, selectAllRows, deselectAllRows, clearSelection, and navigate mirror rendered keyboard and pointer behavior. navigateToCorner(corner, opts?) jumps the active cell to "topLeft" or "bottomRight" (Ctrl+Home / Ctrl+End), optionally extending the range. 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), and getKeyboardNavigationMode() reports the live tree-data navigation mode (non-tree grids always report "grid").

MethodPurpose
getFocusedCell()The active cell as a CellRef (rowPinned set in a pinned band), or null when there is none — independent of DOM focus
setFocusedCell(cell) / setFocusedCell(viewIdx, colIdx)Make a cell active and scroll it into view, by CellRef (the only form that reaches a pinned row) or by page-relative view index and visible-leaf index; no-op for a row or column with no slot
getRowSelectionState() / setRowSelectionState(state)Read or replace the row selection as { selectAll, toggledRowIds } — on the server-side row model a select-all is "every row of the current query except toggledRowIds", the form to hand the server for a bulk operation (server-side select-all)

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
scrollTo({ topPx?, leftPx? })Scroll the body to absolute pixel offsets; an omitted axis stays where it is, leftPx moves the centre section

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
getEditingCell()The cell being edited as { rowId, colId }, or null
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
fillDown() / fillRight()Fill the selection down / right as Ctrl/Cmd+D / Ctrl/Cmd+R do (always a copy); returns the number of cells written
canFillDown() / canFillRight()Whether a fill has cells to write — for a toolbar button's enabled state; re-read on selectionChanged and cellsChanged
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) — or, with none configured, the built-in parser for the column's type — and anything else is stored as the final value. So setCellValue(cell, 99) stores the number 99, setCellValue(cell, "99") stores 99 on a number column too, and setCellValue(cell, "abc") on a number column is refused and writes nothing.

Floating UI and pinned rows​

showTooltip, hideTooltip, openActionFrame, and closeActionFrame address cells by stable row and column IDs; getActionFrameCell() returns the cell with an open ActionFrame, or null. 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.

Overlays​

MethodPurpose
showLoadingOverlay()Show the loading overlay (spinner + loadingMessage) until hideOverlay(); holds the no-rows overlay back meanwhile
showNoRowsOverlay()Show the no-rows overlay now, whatever the row count; the next data change re-decides it
hideOverlay()Hide the loading overlay, and the no-rows overlay unless the view really is empty
api.showLoadingOverlay();
const rows = await fetchOrders();
api.setRowData(rows);
api.hideOverlay();

The React and Angular loading input drives the same overlay. See Loading and empty overlays.

Localization​

MethodPurpose
getLocaleText(key, variableValues?)One of the grid's own strings, translated the way the grid translates it (getLocaleText option, else localeText, else English), with {placeholder}s filled
api.getLocaleText("sortAscending"); // "Sort Ascending"
api.getLocaleText("pageOfTotal", { page: 2, total: 9 }); // "Page 2 of 9"

Use it in custom header components, cell renderers, and menu items so they read like the grid around them. See Localization.

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, and isDestroyed() reports whether it has run — already true while the teardown is in progress, so a cell renderer's destroy can tell the grid is going away. Calls on a destroyed grid do not throw, but they reach a detached renderer; check isDestroyed() rather than relying on what they do.

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 api.getPaginationInfo() read post-filter state. The declarative onFilterChanged option callback wraps it.

quickFilterFindChanged is the find behavior's counterpart to filterChanged: it fires when the search text or case setting changes (reason: "query", which also covers switching behavior), when the user steps to another match ("navigate"), and when the rows or columns under the search move and the matches are re-counted ("model"). Its payload is the whole getFindState() plus that reason, so an app can render its own "3 of 27" counter. A filtering quick filter never fires it — it reports through filterChanged (source: "quickFilter") as before. The declarative option callback is onQuickFilterFindChanged.

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.

rowDoubleClicked and cellDoubleClicked are the double-click counterparts of rowClicked and cellClicked, with the same payloads (node, isGroup, and a group cell's aggregate or label as value). Neither fires when the double-click opened the editor or ran the fill handle; otherwise both fire regardless of cellSelection / editTrigger. Like cellClicked, cellDoubleClicked skips the row-number and checkbox cells. The declarative option callbacks are onRowDoubleClicked and onCellDoubleClicked.

columnWidthsChanged carries changedColIds (empty means every visible column) and a final flag. Dragging a resize handle emits one event per pointer move with final: false, so layout can follow live, then one trailing event with final: true naming every column the drag resized. A double-click autosize and every width change that is not a drag in progress are final: true from the start. Persist column state only on the final event:

api.on("columnWidthsChanged", (ev) => {
if (ev.final) saveColumnState(api.getColumnState());
});

Column identity in payloads and inputs​

Every colId field carried by event payloads (cellsChanged, cellValueChanged, editingChanged, columnsChanged, columnWidthsChanged, cellClicked, cellDoubleClicked, 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.