Grid options
These are the primary options shared by the core, React props, and Angular inputs. Optional fields use documented defaults when omitted.
Data and identity
| Option | Type | Purpose |
|---|---|---|
columnDefs | ColDef[] | Column and column-group definitions |
rowIdKey | string | Field containing a stable row ID. Required in practice on the server-side model: without it (or getRowId) ids change on every block refetch, and the grid warns once |
getRowId | (row) => string | Derive a stable row ID (takes precedence over rowIdKey). The server-side model needs one of the two |
rowDataMode | auto | reset | diff | Whether a replacement rowData array is diffed into the grid (keeping history and page) or re-ingested. Default auto |
asyncTransactionWaitMs | number | Batch window for applyTransactionAsync; defaults to 16 ms. 0 flushes on the next macrotask |
rowModelType | clientSide | serverSide | Select the row model |
serverSideDataSource | IServerSideDataSource | Lazy data-source contract. A failed block emits error and is requested again on the next demand for it (no automatic retry); pair with rowIdKey/getRowId |
serverSideBlockSize | number | Requested block size |
serverSideMaxBlocks | number | Most row blocks kept in memory; least recently used blocks are evicted and refetched on scroll-back. Blocks in or next to the viewport, in flight, or holding an expanded parent are kept. Unset = unbounded. Creation-only |
serverSideAggregationSource | IServerSideDataSource["getAggregates"] | Whole-dataset footer totals; optional when the data source implements getAggregates, and wins over it when both exist |
React and Angular expose rowData through their bindings; core integrations use
api.setRowData().
Layout and columns
| Option | Type | Default |
|---|---|---|
rowHeight | number | The theme's rowHeight param, else 43. Fixed at creation; an explicit value wins over the theme |
headerHeight | number | The theme's headerHeight param, else 43. Height of every header level unless one of the two options below sets it; the body rowHeight never sizes the header. An explicit value wins over the theme, which is read at creation only (updatable at runtime) |
leafHeaderHeight | number | headerHeight. Height of the leaf level, the bottom header row of column labels. Wins over headerHeight (updatable at runtime) |
parentHeaderHeight | number | headerHeight. Height of each column-group level above the leaf level, so the header is (levels - 1) × parentHeaderHeight + leafHeaderHeight tall. Wins over headerHeight (updatable at runtime) |
overscanRowCount | number | Viewport-derived default |
columnVirtualization | boolean | true. Render only the centre (scrolling) columns near the horizontal viewport; pinned and leading columns, the header, the aggregate row and the pinned and sticky rows are always fully rendered, and every column is rendered while a centre column defines colSpan or the grid has no measured width. Creation-only; see Large data sets |
overscanColumnCount | number | 2. Columns rendered beyond each edge of the horizontal viewport under columnVirtualization. Creation-only |
rowNumbers | boolean | false |
defaultColDef | DefaultColDef | {} |
minResizeWidth | number | 75. Smallest width a resize drag reaches; also the floor for content autosize |
maxColumnWidth | number | 420. Cap for content autosize (a declared width, a column's own maxWidth, or a user resize may exceed it) |
columnPanel | boolean | ColumnPanelOptions | false |
enableRtl | boolean | Not set (follows the dir the grid root inherits from the page). true lays the grid out right-to-left, false forces left-to-right; pinned: "left" then means the inline start. Creation-only; see Right-to-left |
Interaction
| Option | Type | Default |
|---|---|---|
cellSelection | boolean | "text" | true (false/"text" also disable the body keyboard cursor — navigation, clipboard, and editing keys) |
rangeSelection | boolean | true |
fillHandle | boolean | FillHandleOptions | true ({ mode: "auto", direction: "xy", lists: [] }; lists are app sequences that cycle like a spreadsheet's custom lists; needs cellSelection: true and rangeSelection) |
fillOperation | (params: FillOperationParams) => FillOperationResult | void | Decide each filled cell's value: { value }, { skipCell: true }, or nothing for the grid's own; see Editing → Fill handle |
columnSelection | boolean | true |
headerKeyboardNavigation | boolean | true (false makes header sort/menu/selection mouse-only) |
rowSelection | boolean | RowSelectionOptions | false (object form configures mode and the checkbox column) |
selectAllScope | "filtered" | "page" | "filtered" (select-all and areAllRowsSelected span the whole filtered set) |
selectionPersistence | "clear" | "keep" | "clear" (row selection on filter/sort/quick-filter changes) |
selectAllRowsOnHeaderClick | boolean | false |
clearSelectionOnBodyClick | boolean | true |
highlightActiveCell | boolean | false |
bodyContextMenu | boolean | getter | true |
multiColumnMenu | false | ({ ctx, columns, items }) => MenuItem[] | Not set (built-in items for a multi-column selection); a getter customizes them, false disables the menu. Also a React prop and an Angular input |
rowInsertionMenu | RowInsertionMenuOptions | Not set; opt-in Insert submenu on client-side row numbers |
RowSelectionOptions accepts mode: "single" | "multiple" (default
"multiple"), checkboxes, and headerCheckbox. Single mode always hides the
header checkbox. Checkbox-column placement uses two independent options:
checkboxColumnPinned: "left" | "right" | null chooses its initial position
(default "left"), while checkboxColumnPinnable controls whether users can
change that position later (default true).
These nested row-selection options reconcile live in both React and Angular;
changing them does not remount the grid or replace its API instance.
Editing
| Option | Type | Default |
|---|---|---|
editTrigger | doubleClick | singleClick | none | doubleClick |
suppressKeyboardEdit | boolean | false |
suppressTypeToEdit | boolean | false |
moveAfterEdit | boolean | true |
commitOnBlur | boolean | true |
reevaluateOnEdit | boolean | true |
undoLimit | number | 100 |
Sort, filter, and pagination
| Option | Type | Purpose |
|---|---|---|
initialSort | { colId, dir }[] | Ordered initial client-side sort |
showSortPriority | multi | always | never | Priority badge behavior |
quickFilter | boolean | QuickFilterOptions | Global client-side search — filters rows, or highlights matching cells with behavior: "find" |
filterDebounceMs | number | Default column-filter debounce |
pagination | boolean | Enable footer pagination |
paginationControls | PaginationControlsOptions | Page-picker style plus visible pagination controls and their order |
pageSize | number | Initial page size |
pageSizes | number[] | User-selectable page sizes |
resetPageOn | ("filter" | "sort" | "quickFilter")[] | Model changes that jump back to page 1 (default []) |
pageSizes is normalized at init (sorted ascending, non-positive entries
dropped). If pageSize is not in the list it is added automatically so the
page-size selector can display it — with a console warning when pageSizes was
set explicitly.
The page picker remains a <select> by default. Use numbered buttons and configure
the footer controls by supplying their visual (and keyboard) order:
paginationControls: {
pageSelection: "buttons",
showPageLabel: false,
controls: ["previousPage", "pageSelector", "nextPage", "pageSize"],
maxPageButtons: 7,
}
Available controls are "pageSize", "firstPage", "previousPage",
"pageSelector", "nextPage", and "lastPage". Omitted controls are hidden.
Set showPageLabel: false to remove the visible Page label while preserving
accessible names on the select or numbered-button group.
Numbered pagination keeps the first and last known pages visible, centers a bounded
window around the current page, and inserts ellipses when pages are skipped.
paginationControls.aggregateScope governs the footer's aggregate-scope control while
Entire dataset is unavailable: the server-side row model without a server aggregation
source (see whole-dataset totals).
By default the choice stays in the list greyed out, with a tooltip saying what would enable
it; "hidden" drops it from the list and from the footer's overflow menu.
paginationControls: {
aggregateScope: {
whenUnavailable: "disabled", // default; "hidden" removes the choice instead
unavailableMessage: "Totals over all orders are not available in this view.",
},
}
The footer adapts to its own width, on the same ladder the toolbar uses — see
Narrow bars. Its rungs, cheapest first: the text beside each control
("Rows per page", "Aggregate", "Page") is hidden; the first and last page buttons
go, since the page picker beside them already reaches any page; rows-per-page moves into
the footer's overflow menu; then the aggregate scope; then the sheet strip's +. Page
navigation itself never gives way — a footer that cannot page is not a footer — so a
footer still short of room scrolls instead. Trimming controls is how an application
spends that width on something else — a wide sheet-tab strip, for instance.
paginationControls: { responsive: "collapse" } // "collapse" (default) | "scroll" | false
Filter and quick-filter changes used to reset pagination to page 1 (and sort
changes never did). The default is now resetPageOn: [] — no model change
resets the page. The grid keeps the user's current page; when the change
shrinks the row count past it, it clamps to the last page instead of showing
an empty view. Add "filter", "quickFilter", and/or "sort" to restore the
jump-to-page-1 behavior for that trigger — the policy is now symmetric and
per-trigger. (On the server-side row model the page snaps back once the total
row count is known; the quick filter is client-side only.)
Quick filter
quickFilter: true enables the search with every default below; an object
overrides them. See Quick filter for
what each one does to the search itself.
| Option | Type | Default | Purpose |
|---|---|---|---|
mode | "onDemand" | "always" | "onDemand" | Open the floating widget with Ctrl/Cmd+F, or pin it open |
behavior | "filter" | "find" | "filter" | Hide non-matching rows, or leave every row and highlight matching cells |
showBehaviorToggle | boolean | false | Offer the filter/find choice in the options popover |
matchMode | "multiTerm" | "substring" | "wholeCell" | "multiTerm" | How the input is matched against a cell |
caseSensitive | boolean | false | Case-sensitive comparison |
debounceMs | number | 150 | Delay from keystroke to search |
showOptions | boolean | true | Expose the match-mode / match-case popover to end users |
showLayoutOptions | boolean | false | Also expose Anchor and "Keep filter when closed" in that popover |
clearOnClose | boolean | true | Dismissing the widget clears the search; false keeps it applied behind a re-openable pill |
position | QuickFilterPositionOptions | see below | Where the floating widget sits |
End-user picks in the options popover are sticky for the session and are not written back to these options.
position places the widget inside the grid, relative to the bottom of the
header:
| Option | Type | Default | Purpose |
|---|---|---|---|
anchor | "left" | "right" | "right" | Which horizontal edge the widget is pinned to |
offsetX | number | 8 | Inset from that edge, added on top of the scrollbar gutter on the right |
offsetTop | number | 6 | Gap between the header's bottom edge and the widget |
While a find search is active the widget may sit at the opposite edge, to
uncover an active match that no scroll can move out from under it — see
Keeping matches visible.
That never rewrites anchor.
position, clearOnClose and showLayoutOptions are floating-only: a widget
hosted in the toolbar is laid out by the bar instead.
Grouping and hierarchy
| Option | Type | Purpose |
|---|---|---|
groupDisplayType | singleColumn | multipleColumns | groupRows | Label layout |
groupDefaultExpanded | number | Initial expanded depth; -1 means all |
groupSortMode | local | hierarchy | global | Client-side group ordering |
groupRowsSelectable | boolean | Include generated groups in selection |
groupRowsSticky | boolean | Dock the active ancestor chain |
groupChildCount | boolean | GroupChildCountGetter | The count after a group label: true (default) the row count, false none, or a function returning a number, a string, or null per group — given the node, the default count, the group's aggregates by colId, and (server-side) the raw group row. One resolver feeds cells, sticky rows, the clipboard, and both exports. Live in both wrappers |
groupTotalRow | "top" | "bottom" | A footer (subtotal) row per expanded group, under its header or after its last row, repeating the group's aggregates beside a "Total <group>" label. Client-side row grouping only. Live in both wrappers. See group footers |
grandTotalRow | "top" | "bottom" | One grand-total row over every filtered row, first or last in the grouped list. Client-side row grouping only. Live in both wrappers |
groupTotalRowLabel | (params: GroupTotalRowLabelParams) => string | Replaces the footer label ("Total <group>", "Grand Total"); given the footer node, groupNode, grandTotal, groupLabel, defaultLabel and aggregates by colId. Live in both wrappers |
aggFuncs | Record<string, AggregateFunction> | Custom aggregate functions by name, usable wherever a built-in type is: ColDef.aggFunc, api.setAggregates, the aggregate menus (listed after the built-ins, by name), saved views and pivot measures. Each gets { values, rows, colId } — raw values with blanks included — and its result renders through the column's formatter; exports write it as a static value. Creation-only. See aggregates |
treeData | TreeDataOptions | Enable a row hierarchy: path, parent, or children on the client-side model, or server (per-row hasChildren, children fetched per parent) on the server-side one |
pivotMode | boolean | Start in pivot mode (client-side; exclusive with tree data). Live in both wrappers — a prop/input change toggles the mode |
pivotColumns | string[] | Columns to pivot on, by colId, in level order. Live in both wrappers |
pivotResultColumnDef | Partial<ColDef> | Overlay for every generated pivot value column |
maxPivotColumns | number | Cap on generated pivot columns (default 200; truncates + fires pivotColumnLimitReached). A target, not an exact ceiling — truncation is per pivot value, so the count can land under or over it |
pivotColumnMoveMode | "measures" | "free" | What dragging a generated pivot column does: reorder the measures everywhere (default) or arrange leaves freely, splitting groups (updatable at runtime) |
Event callbacks
Each on* option is a convenience over one core event — passing it is the same as
api.on(eventName, handler) after creation, and neither replaces the other.
| Option | Event | Fires when |
|---|---|---|
onCellClicked / onRowClicked | cellClicked / rowClicked | A body cell or row is clicked |
onCellDoubleClicked / onRowDoubleClicked | cellDoubleClicked / rowDoubleClicked | A body cell or row is double-clicked (not when it opened the editor) |
onCellValueChanged | cellValueChanged | A cell's stored value changes, on any write path |
onSelectionChanged | selectionChanged | The selection changes |
onSortChanged | columnsChanged (reason "sort") | The sort changes |
onFilterChanged | filterChanged | The effective row filter changes |
onQuickFilterFindChanged | quickFilterFindChanged | The find match count or active match changes |
onHistoryChanged | historyChanged | The undo/redo stacks move |
onColumnsChanged | columnsChanged | Columns change: definitions, state restores, pin, visibility, order, sort, filter, grouping, pivot (reason says which) |
onPaginationChanged | paginationChanged | Page, page size or totals may have changed; the payload is api.getPaginationInfo() |
onError | error | A row-model request fails (code: "row_model_error") |
onRowGroupOpened | rowGroupOpened | Row groups or tree nodes expand or collapse: one call per gesture or API call, after the rebuild, with the changed groupIds, the new expanded state and source |
onColumnGroupOpened | columnGroupOpened | Column groups open or close (header expander, setColumnGroupState): one call per direction, with source |
onFirstDataRendered | firstDataRendered | Once per grid, after the first paint that shows a row (later for a grid created empty) |
onRowDataUpdated | rowDataUpdated | The row data changes: reason is "rowData", "transaction" or "serverSide" (a block load or purge) |
onBodyScroll | bodyScroll | Every body scroll tick, with scrollTopPx, scrollLeftPx and direction |
onColumnWidthsChanged | columnWidthsChanged | Column widths change; final is false during a resize drag, source as on onColumnsChanged |
onEditingChanged | editingChanged | A cell editor starts, commits, is rejected, cancelled or stops |
onFocusChanged | focusChanged | The body cursor (focused cell) moves or clears |
onHeaderFocusChanged | headerFocusChanged | The header keyboard cursor moves, enters or leaves the header |
onRowsChanged | rowsChanged | The displayed rows are re-laid out (data, grouping, expansion, a page or block) |
onCellsChanged | cellsChanged | Cells need repainting: values, formats, styles, an edit commit or a refresh |
onModelUpdated | modelUpdated | The row model re-derived its view (reason says why) |
onAggregateChanged | aggregateChanged | Aggregate values, model or scope change |
onPivotChanged | pivotChanged | Pivot mode toggles or the pivot columns change |
onPivotColumnLimitReached | pivotColumnLimitReached | Generated pivot columns start or stop being truncated by maxPivotColumns |
onOverlayShow | overlayShow | The loading or no-rows overlay is shown or taken down |
onTooltipShow / onTooltipHide | tooltipShow / tooltipHide | A body, header or grid-UI tooltip opens or closes |
onActionFrameChanged | actionFrameChanged | A cell's ActionFrame opens or closes |
onKeyboardNavigationModeChanged | keyboardNavigationModeChanged | The tree-data keyboard navigation mode switches |
onViewportChanged | viewportChanged | The grid initialises (emitted once; follow scrolling with onBodyScroll) |
Every event in the GridEventMap has an option here, a React <Grid> prop of the same name,
and an <awb-grid> output named like the event ((rowGroupOpened), (bodyScroll), …) — the
one exception is error, whose output is (gridError). Like the other callbacks they are read
at creation; the framework bindings bridge them, so a changed prop or a re-bound output is live.
rowGroupOpened reports source: "user" for the group chevron and the tree-data keyboard
shortcuts and "api" for setAllGroupsExpanded / setGroupExpanded, view restores and the
grid's own reveals (a find match, a row scrolled to by id). Only the rows whose state changed
are listed, and a call that changes nothing fires nothing. rowDataUpdated does not fire for
sorting, filtering, grouping or expansion — those move rows without changing the data.
onPaginationChanged also fires after operations that leave every field as it was (a
re-sort, a regroup), so compare with the previous payload when only real changes matter.
onError is a notification only: the grid's handling of the failure is unchanged (see
Failed requests).
Telling user changes from your own
columnsChanged, columnWidthsChanged and onSortChanged carry source:
"user"— a gesture made through the grid's UI: a header click, drag, resize handle, double-click autosize or header keyboard shortcut; a column-menu item; the column panel; the toolbar's grouping, pivot and sort controls."api"— everything else:IGridAPIcalls, saved-view andapplyColumnStaterestores,columnDefsupdates, server-sent schemas, and the grid's own follow-up adjustments. A custom menu item'sonClickthat calls the API reports"api"too: the source is the path the change took, not what prompted it.
Persist column state on "user" changes only, and restoring it never writes back:
const grid = createGrid(el, {
columnDefs,
onColumnsChanged: (event) => {
if (event.source === "user") saveLayout(grid.getColumnState());
},
});
grid.on("columnWidthsChanged", (event) => {
// `final` is false while a resize handle is still being dragged.
if (event.source === "user" && event.final) saveLayout(grid.getColumnState());
});
Presentation and integration
| Option | Type | Purpose |
|---|---|---|
theme | GridTheme | Per-instance visual theme |
icons | GridIconMap | Named icon overrides |
tooltip | boolean | TooltipOptions | Global tooltip behavior |
getRowPresentation | (params) => RowPresentation | Row/cell styling defaults, tooltip defaults, editability, ARIA description/busy state, and metadata |
toolbar | GridToolbarOptions | Opt-in toolbar sections, plus responsive (see Narrow bars) |
savedViews | SavedViewsOptions | Application-owned view persistence |
sheets | SheetsOptions | Spreadsheet-style footer tabs (Data + pivot sheets over one row model); application-owned list |
suppressStyleInjection | boolean | Use imported CSS instead |
styleNonce | string | CSP nonce for injected styles |
loadingMessage | string | Loading overlay copy (default "Loading data..."; updatable at runtime) |
noRowsMessage | string | Empty-data copy (default "No rows to show"; updatable at runtime) |
pivotNoValuesMessage | string | Copy of the header hint shown while pivot mode has no aggregates |
pivotEmptyMessage | string | Copy of the empty state shown while pivot mode has no roles configured at all |
localeText | Partial<GridLocaleText> | Translations for the grid's own strings, by key; missing keys stay English (creation-only; see Localization) |
getLocaleText | (params: LocaleTextParams) => string | Translate any grid string in code (plurals, ICU, a translation library); wins over localeText (creation-only) |
Narrow bars
Both bars — the toolbar above the header and the footer below the body — cope with a width
their controls do not fit by the same rule: nothing is ever clipped, overlapped, or
compressed. Every control is laid out at its natural size in one of its presentation
stages, or it moves into that bar's overflow menu (⋮); a bar that runs out of stages
scrolls, so nothing becomes unreachable.
The toolbar's rungs, cheapest first:
- every button caption at once — the tooltip on each button carries the label it lost;
- the quick-filter input narrows — a field you can still read and type in costs less than the name of a column you grouped by;
- Grouped by / Sort by chips fold from the end into a
+Nchip, one at a time; - each chip section becomes a summary button (
Grouped by 3) that opens the same drag-reorder editor as a popover — and stays a drop target for a column dragged out of the header; - Export, Pivot, Views, then the two chip sections move into the
⋮; - the quick filter becomes a search icon that expands the box back over the bar in place;
- Columns last, because the column panel is the escape hatch to everything else.
Two rules keep that honest. A control that holds focus, or that carries state the user is
relying on (a quick filter with a query), is not displaced — the next rung goes
instead — so nothing collapses out from under someone mid-use. And when something is
displaced, its state does not vanish quietly: the ⋮ wears a dot while anything inside it
is active, and focus follows a displaced control to the button that now holds it.
A settled bar has no hole in it. The last rung applied usually frees more room than was needed, and rather than leaving that as blank space in the middle of the bar, one control takes it: the search field, which stretches into it (up to a limit), or — with no quick filter configured — the last chip section, which widens its drop zone instead.
The order is fixed. An application that wants a different one configures fewer controls rather than re-ordering the ladder; every ordering is a degradation path that has to hold up at every width. What is configurable is whether the ladder runs at all:
toolbar: { grouping: true, sorting: true, quickFilter: true, responsive: "collapse" }
"collapse" (the default) walks the ladder above. "scroll" leaves every control at full
size and scrolls the bar as soon as they do not fit — simplest to reason about, at the cost
of controls sitting off-screen. false lays the bar out and lets it clip, for an
application that guarantees its own width. paginationControls.responsive says the same
for the footer.
The package's TypeScript declarations remain the source of truth for less common callbacks and detailed nested option types.
Row presentation
Use getRowPresentation when a status belongs to the whole row rather than a
particular field:
getRowPresentation: ({ rowId }) => {
const error = errors.get(rowId);
if (!error) return undefined;
return {
rowClass: "row-error",
cellClass: "cell-in-error-row",
cellStyle: { backgroundColor: "var(--error-row-bg)" },
editable: false,
tooltip: {
content: error.message,
component: ErrorTooltip,
componentParams: { showCode: true },
options: { mode: "follow", escapeRootClip: true },
},
accessibility: {
description: `Row contains an error: ${error.message}`,
busy: false,
},
metadata: { status: "error", error },
};
}
rowClass/rowStyle style every row-container fragment and compose with the
older getRowClass/getRowStyle hooks. cellClass is combined with each
column's cellClass; cellStyle is the default object and the column wins only
conflicting properties. The resolved presentation is also available to cell
renderers, cell-style callbacks, and tooltip components as rowPresentation.
editable: false prevents user editing throughout the row. It is a veto: the
row cannot make a column editable unless that column is already editable, and
a column with editable: false remains non-editable. For a deliberate exception,
set inheritRowPresentation.editable: false on that column. Programmatic writes
through setCellValue and setCellValues are not blocked by UI editability.
Tooltip layers resolve by specificity:
- Content: explicit column getter/field → row content →
defaultColDef→ clipped-cell text. - Component: explicit column component → row component →
defaultColDef→ built-in text. - Options, field by field: grid →
defaultColDef→ row → explicit column.
This lets a column replace its message or placement while retaining the row's
component and other behavior. interactive: true always forces anchored mode;
placement is relevant only while anchored. A column can opt out selectively:
{
key: "actions",
label: "Actions",
inheritRowPresentation: {
tooltip: false,
cellClass: false,
cellStyle: false,
editable: false,
},
}
When the callback reads external state (a pending-ID set, for example), call
api.refreshRowPresentation() after changing it. Row transactions already
repaint affected rendered rows. React and Angular row tooltip components use
the framework-aware ReactGetRowPresentation and NgGetRowPresentation types.