Skip to main content

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​

OptionTypePurpose
columnDefsColDef[]Column and column-group definitions
rowIdKeystringField 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) => stringDerive a stable row ID (takes precedence over rowIdKey). The server-side model needs one of the two
rowDataModeauto | reset | diffWhether a replacement rowData array is diffed into the grid (keeping history and page) or re-ingested. Default auto
asyncTransactionWaitMsnumberBatch window for applyTransactionAsync; defaults to 16 ms. 0 flushes on the next macrotask
rowModelTypeclientSide | serverSideSelect the row model
serverSideDataSourceIServerSideDataSourceLazy 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
serverSideBlockSizenumberRequested block size
serverSideMaxBlocksnumberMost 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
serverSideAggregationSourceIServerSideDataSource["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​

OptionTypeDefault
rowHeightnumberThe theme's rowHeight param, else 43. Fixed at creation; an explicit value wins over the theme
headerHeightnumberThe 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)
leafHeaderHeightnumberheaderHeight. Height of the leaf level, the bottom header row of column labels. Wins over headerHeight (updatable at runtime)
parentHeaderHeightnumberheaderHeight. 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)
overscanRowCountnumberViewport-derived default
columnVirtualizationbooleantrue. 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
overscanColumnCountnumber2. Columns rendered beyond each edge of the horizontal viewport under columnVirtualization. Creation-only
rowNumbersbooleanfalse
defaultColDefDefaultColDef{}
minResizeWidthnumber75. Smallest width a resize drag reaches; also the floor for content autosize
maxColumnWidthnumber420. Cap for content autosize (a declared width, a column's own maxWidth, or a user resize may exceed it)
columnPanelboolean | ColumnPanelOptionsfalse
enableRtlbooleanNot 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​

OptionTypeDefault
cellSelectionboolean | "text"true (false/"text" also disable the body keyboard cursor — navigation, clipboard, and editing keys)
rangeSelectionbooleantrue
fillHandleboolean | FillHandleOptionstrue ({ 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 | voidDecide each filled cell's value: { value }, { skipCell: true }, or nothing for the grid's own; see Editing → Fill handle
columnSelectionbooleantrue
headerKeyboardNavigationbooleantrue (false makes header sort/menu/selection mouse-only)
rowSelectionboolean | RowSelectionOptionsfalse (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)
selectAllRowsOnHeaderClickbooleanfalse
clearSelectionOnBodyClickbooleantrue
highlightActiveCellbooleanfalse
bodyContextMenuboolean | gettertrue
multiColumnMenufalse | ({ 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
rowInsertionMenuRowInsertionMenuOptionsNot 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​

OptionTypeDefault
editTriggerdoubleClick | singleClick | nonedoubleClick
suppressKeyboardEditbooleanfalse
suppressTypeToEditbooleanfalse
moveAfterEditbooleantrue
commitOnBlurbooleantrue
reevaluateOnEditbooleantrue
undoLimitnumber100

Sort, filter, and pagination​

OptionTypePurpose
initialSort{ colId, dir }[]Ordered initial client-side sort
showSortPrioritymulti | always | neverPriority badge behavior
quickFilterboolean | QuickFilterOptionsGlobal client-side search — filters rows, or highlights matching cells with behavior: "find"
filterDebounceMsnumberDefault column-filter debounce
paginationbooleanEnable footer pagination
paginationControlsPaginationControlsOptionsPage-picker style plus visible pagination controls and their order
pageSizenumberInitial page size
pageSizesnumber[]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
Behavior change

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.

OptionTypeDefaultPurpose
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
showBehaviorTogglebooleanfalseOffer the filter/find choice in the options popover
matchMode"multiTerm" | "substring" | "wholeCell""multiTerm"How the input is matched against a cell
caseSensitivebooleanfalseCase-sensitive comparison
debounceMsnumber150Delay from keystroke to search
showOptionsbooleantrueExpose the match-mode / match-case popover to end users
showLayoutOptionsbooleanfalseAlso expose Anchor and "Keep filter when closed" in that popover
clearOnClosebooleantrueDismissing the widget clears the search; false keeps it applied behind a re-openable pill
positionQuickFilterPositionOptionssee belowWhere 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:

OptionTypeDefaultPurpose
anchor"left" | "right""right"Which horizontal edge the widget is pinned to
offsetXnumber8Inset from that edge, added on top of the scrollbar gutter on the right
offsetTopnumber6Gap 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​

OptionTypePurpose
groupDisplayTypesingleColumn | multipleColumns | groupRowsLabel layout
groupDefaultExpandednumberInitial expanded depth; -1 means all
groupSortModelocal | hierarchy | globalClient-side group ordering
groupRowsSelectablebooleanInclude generated groups in selection
groupRowsStickybooleanDock the active ancestor chain
groupChildCountboolean | GroupChildCountGetterThe 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) => stringReplaces the footer label ("Total <group>", "Grand Total"); given the footer node, groupNode, grandTotal, groupLabel, defaultLabel and aggregates by colId. Live in both wrappers
aggFuncsRecord<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
treeDataTreeDataOptionsEnable 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
pivotModebooleanStart in pivot mode (client-side; exclusive with tree data). Live in both wrappers — a prop/input change toggles the mode
pivotColumnsstring[]Columns to pivot on, by colId, in level order. Live in both wrappers
pivotResultColumnDefPartial<ColDef>Overlay for every generated pivot value column
maxPivotColumnsnumberCap 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.

OptionEventFires when
onCellClicked / onRowClickedcellClicked / rowClickedA body cell or row is clicked
onCellDoubleClicked / onRowDoubleClickedcellDoubleClicked / rowDoubleClickedA body cell or row is double-clicked (not when it opened the editor)
onCellValueChangedcellValueChangedA cell's stored value changes, on any write path
onSelectionChangedselectionChangedThe selection changes
onSortChangedcolumnsChanged (reason "sort")The sort changes
onFilterChangedfilterChangedThe effective row filter changes
onQuickFilterFindChangedquickFilterFindChangedThe find match count or active match changes
onHistoryChangedhistoryChangedThe undo/redo stacks move
onColumnsChangedcolumnsChangedColumns change: definitions, state restores, pin, visibility, order, sort, filter, grouping, pivot (reason says which)
onPaginationChangedpaginationChangedPage, page size or totals may have changed; the payload is api.getPaginationInfo()
onErrorerrorA row-model request fails (code: "row_model_error")
onRowGroupOpenedrowGroupOpenedRow 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
onColumnGroupOpenedcolumnGroupOpenedColumn groups open or close (header expander, setColumnGroupState): one call per direction, with source
onFirstDataRenderedfirstDataRenderedOnce per grid, after the first paint that shows a row (later for a grid created empty)
onRowDataUpdatedrowDataUpdatedThe row data changes: reason is "rowData", "transaction" or "serverSide" (a block load or purge)
onBodyScrollbodyScrollEvery body scroll tick, with scrollTopPx, scrollLeftPx and direction
onColumnWidthsChangedcolumnWidthsChangedColumn widths change; final is false during a resize drag, source as on onColumnsChanged
onEditingChangededitingChangedA cell editor starts, commits, is rejected, cancelled or stops
onFocusChangedfocusChangedThe body cursor (focused cell) moves or clears
onHeaderFocusChangedheaderFocusChangedThe header keyboard cursor moves, enters or leaves the header
onRowsChangedrowsChangedThe displayed rows are re-laid out (data, grouping, expansion, a page or block)
onCellsChangedcellsChangedCells need repainting: values, formats, styles, an edit commit or a refresh
onModelUpdatedmodelUpdatedThe row model re-derived its view (reason says why)
onAggregateChangedaggregateChangedAggregate values, model or scope change
onPivotChangedpivotChangedPivot mode toggles or the pivot columns change
onPivotColumnLimitReachedpivotColumnLimitReachedGenerated pivot columns start or stop being truncated by maxPivotColumns
onOverlayShowoverlayShowThe loading or no-rows overlay is shown or taken down
onTooltipShow / onTooltipHidetooltipShow / tooltipHideA body, header or grid-UI tooltip opens or closes
onActionFrameChangedactionFrameChangedA cell's ActionFrame opens or closes
onKeyboardNavigationModeChangedkeyboardNavigationModeChangedThe tree-data keyboard navigation mode switches
onViewportChangedviewportChangedThe 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: IGridAPI calls, saved-view and applyColumnState restores, columnDefs updates, server-sent schemas, and the grid's own follow-up adjustments. A custom menu item's onClick that 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​

OptionTypePurpose
themeGridThemePer-instance visual theme
iconsGridIconMapNamed icon overrides
tooltipboolean | TooltipOptionsGlobal tooltip behavior
getRowPresentation(params) => RowPresentationRow/cell styling defaults, tooltip defaults, editability, ARIA description/busy state, and metadata
toolbarGridToolbarOptionsOpt-in toolbar sections, plus responsive (see Narrow bars)
savedViewsSavedViewsOptionsApplication-owned view persistence
sheetsSheetsOptionsSpreadsheet-style footer tabs (Data + pivot sheets over one row model); application-owned list
suppressStyleInjectionbooleanUse imported CSS instead
styleNoncestringCSP nonce for injected styles
loadingMessagestringLoading overlay copy (default "Loading data..."; updatable at runtime)
noRowsMessagestringEmpty-data copy (default "No rows to show"; updatable at runtime)
pivotNoValuesMessagestringCopy of the header hint shown while pivot mode has no aggregates
pivotEmptyMessagestringCopy of the empty state shown while pivot mode has no roles configured at all
localeTextPartial<GridLocaleText>Translations for the grid's own strings, by key; missing keys stay English (creation-only; see Localization)
getLocaleText(params: LocaleTextParams) => stringTranslate 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:

  1. every button caption at once — the tooltip on each button carries the label it lost;
  2. 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;
  3. Grouped by / Sort by chips fold from the end into a +N chip, one at a time;
  4. 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;
  5. Export, Pivot, Views, then the two chip sections move into the ⋮;
  6. the quick filter becomes a search icon that expands the box back over the bar in place;
  7. 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.