The grid supports editing grouped data when using the Client-Side Row Model. This page covers making group row cells editable, distributing edited values to descendant rows, and refreshing the grouping hierarchy after edits.
Editing Group Row Cells Copy Link
Set groupRowEditable on any column to make its group row cells editable. The grid then distributes the edited value to descendant rows using the built-in distribution. To customise this, set groupRowValueSetter to an options object, a callback, or false to disable distribution.
In the example below, double-click any group row's Sales cell to edit it. The new total spreads equally across the group's children, recursing through nested groups.
groupRowEditable accepts a boolean or a callback. The callback runs only for group rows (rowNode.group === true); leaf rows continue to honour editable. When a column sets both properties, the grid uses whichever matches the current row type, allowing separate rules for group rows and leaf rows.
Works like editable, but is evaluated only for group rows. When provided, group rows use
this property instead of editable. Set to true to make group row cells editable, or use
a callback to control editability per row.
When groupRowEditable is defined and no explicit groupRowValueSetter is provided,
the built-in distributeGroupValue is used automatically.
Columns with groupRowEditable or groupRowValueSetter do not require field or
valueSetter — the group row value setter handles the edit entirely.
Note: if groupRowValueSetter resolves to false or null (via distribution: false,
a per-aggFunc record entry, or groupRowValueSetter: false), the cell is treated as not
editable even when groupRowEditable is true. |
Controls how a group row value edit is distributed to descendant rows. true: Uses the built-in distributeGroupValue with default settings. groupRowEditable is defined and groupRowValueSetter is not set.false: Explicitly disables group row value distribution and makes the cell not editable, groupRowEditable is defined.GroupRowValueSetterParams and pushes field or valueSetter — the callback
handles the edit entirely.GroupRowValueSetterOptions distribution resolves to false or null for the column's aggFunc,
the cell is treated as not editable (overriding groupRowEditable).
Fires for every setDataValue call when active, regardless of groupRowEditable. |
Built-in Value Distribution Copy Link
The built-in distribution adjusts each child so the column's aggFunc produces the new total. Pass an options object to groupRowValueSetter to customise the strategy or precision.
const gridOptions = {
columnDefs: [
{
field: 'sales',
aggFunc: 'sum',
editable: true,
groupRowEditable: true,
// Built-in distribution: divides the sum equally among children.
// precision: 0 rounds child values to integers and spreads the remainder.
groupRowValueSetter: { precision: 0 },
},
],
// other grid options ...
} Distribution Strategies Copy Link
The distribution option controls how the edited value is spread across children. When omitted, the strategy is chosen automatically based on the column's aggFunc:
| Strategy | Behaviour | Default for |
|---|---|---|
'uniform' | Divides the new value equally among all children. | sum |
'overwrite' | Writes the new value directly to every child. | avg, no aggFunc |
'percentage' | Scales each child proportionally, preserving relative weights. Falls back to 'uniform' when the current total is zero. | — |
'increment' | Distributes only the difference (newValue − oldValue) among children, adding it to current values. | — |
Editing is disabled by default for count, min, max, first, last, and custom aggregation functions. These functions have no unique inverse, so the grid cannot choose a safe default. To enable editing for them:
- Set
distributionto'overwrite'to write the new value directly to every child. - Provide a per-aggFunc record entry with
'overwrite',true, or a custom callback (see Per-Aggregation Strategies). - Assign a callback to
groupRowValueSetterfor full control.
const gridOptions = {
columnDefs: [
{
field: 'price',
aggFunc: 'min',
editable: true,
groupRowEditable: true,
// 'overwrite' writes the edited value to every child
groupRowValueSetter: { distribution: 'overwrite' },
},
{
field: 'quantity',
aggFunc: 'count',
editable: true,
groupRowEditable: true,
// Per-aggFunc record: enable only count with overwrite
groupRowValueSetter: { distribution: { count: 'overwrite' } },
},
],
// other grid options ...
}Setting distribution to true uses built-in defaults for sum, avg, no-aggFunc columns, and custom aggregation functions. The count, min, max, first, and last functions stay disabled even with true; use 'overwrite', a per-aggFunc record entry, or a callback to enable them.
Setting distribution to false or null suppresses distribution and makes the cell not editable, even when groupRowEditable is true.
For avg, every strategy adjusts child values so the children's average equals the edited value.
The precision option rounds the values written to child rows, not the group row's displayed value. The group value is re-computed by the column's aggFunc from the rounded children. For sum this is always exact. For avg the re-aggregated value may differ: distributing precision: 0 across 3 children produces integers, but their average (10 / 3 ≈ 3.33) is not. Use a Value Formatter to control how the group row displays the re-aggregated value.
Distribution Options Copy Link
Properties available on the GroupRowValueSetterOptions<TData = any, TValue = any, TContext = any> interface.
Distribution strategy or per-aggregation-function strategy map. As a string: applies the chosen GroupRowValueSetterDistribution strategy to all aggregation functions. As true: enables distribution using built-in defaults for distributable aggregation functions and custom aggFuncs (which get 'overwrite'). Note: count/min/max/first/last are only enabled via explicit per-aggFunc record entries. Useful for overriding false/null from defaultColDef in deep-merge scenarios. As false or null: suppresses distribution and makes the cell not editable (overriding groupRowEditable). As a record: maps aggFunc names to individual strategies, options objects, or custom callbacks. Unmatched aggFuncs fall through to default, then to the built-in defaults.
Example |
Fallback for aggFuncs that don't have a specific distribution strategy. When distribution is a record, applies to aggFuncs not listed in the record. When distribution is omitted, applies only to custom (non-built-in) aggFuncs. Ignored when distribution is a string (all aggFuncs use the specified strategy). Accepts the same values as record entries:'overwrite'). false or null to suppress distribution and make unmatched aggFunc cells not editable. Example |
Number of decimal places to round values written to child rows during distribution. Spreads any rounding remainder across children so their total matches exactly. 0 — integers (e.g. 10 / 3 → [4, 3, 3]) 2 — two decimals (e.g. 10 / 3 → [3.34, 3.33, 3.33]) false — disable rounding (overrides auto-detect) undefined (default) — auto-detect from the column definition: cellEditorParams.precision if set, 0 if cellEditorParams.step is a whole number, no rounding otherwise. Note: the group row's displayed value is re-computed by the aggFunc after distribution. For sum, the sum of rounded children always honours the same precision. For other aggregation functions like avg, the re-aggregated value may not — for example, the average of integers is not necessarily an integer. Ignored for bigint columns — bigint values are always distributed as integers.
Example |
Reads a child's current value during distribution. Default: node.getDataValue(column, 'value'). Override to read from a custom data structure or computed field.
Returns: The child's current value. Example |
Writes a distributed value to a child. Default: node.setDataValue(column, value, 'data'). Override to write to a custom data structure or apply transformations.
Returns: true if the value was changed, false otherwise.
Example |
Merging with Default Column Definitions Copy Link
When groupRowValueSetter is set as an options object on both defaultColDef and a column, the grid deep merges the two. The column's properties take precedence; anything not specified on the column is inherited from the default. This includes nested distribution records.
const gridOptions = {
defaultColDef: {
editable: true,
groupRowEditable: true,
// Default: round child values to integers, disable count
groupRowValueSetter: { precision: 0, distribution: { count: false } },
},
columnDefs: [
{
field: 'sales',
aggFunc: 'sum',
// Column-level override: use percentage distribution.
// precision: 0 is inherited from defaultColDef.
groupRowValueSetter: { distribution: 'percentage' },
},
{
field: 'quantity',
aggFunc: 'count',
// Override defaultColDef's false with true to enable editing for count.
groupRowValueSetter: { distribution: { count: true } },
},
{
field: 'profit',
aggFunc: 'sum',
// No column-level override — inherits { precision: 0 } from defaultColDef.
},
],
// other grid options ...
}Deep merging only applies when both the defaultColDef and the column define groupRowValueSetter as plain objects. If the column defines a callback function, it replaces the default entirely.
Multiple Aggregation Functions Copy Link
The following example mixes built-in strategies, a non-aggregated column, and custom callbacks. Double-click any group row cell to edit it:
- Salary (
sum): divides the new total equally among children, rounded to integers (precision: 0). Editing 300 across 3 children produces 100 each. - Bonus (
avg): overwrites every child with the edited value. Editing the average to 15 sets all children's bonus to 15. - Projects (no
aggFunc): overwrites every child with the edited value. The group cell is blank but still editable. - Rate % (custom callback): computes each leaf child's bonus as
salary × rate / 100. Entering 20 sets each child's bonus to 20% of their salary. Usesnode.getAggregatedChildren(column, true)to reach all descendant leaf rows directly. - SumSq (custom
sumOfSquaresaggFunc): a callback sets each child to√(newValue / count), the inverse of sum-of-squares.
Per-Aggregation Strategies Copy Link
When several columns share a groupRowValueSetter (e.g. via defaultColDef) but use different aggregation functions, set distribution to a record keyed by aggFunc name. Each entry can be a strategy string, an options object, a custom callback, true for built-in defaults, or false/null to suppress distribution for that aggFunc:
const myCustomAgg = (params) => {
let total = 0;
for (const value of params.values) {
total += Number(value) || 0;
}
return total;
};
// Optional: inverse enables incremental aggregation on row changes
myCustomAgg.inverse = (params) => {
let total = Number(params.result) || 0;
for (const value of params.values) {
total -= Number(value) || 0;
}
return total;
};
const gridOptions = {
aggFuncs: {
myCustomAgg,
},
defaultColDef: {
editable: true,
groupRowEditable: true,
groupRowValueSetter: {
precision: 0,
distribution: {
sum: 'percentage', // strategy string
avg: { distribution: 'increment' }, // options object
myCustomAgg: (params) => { // custom callback function
for (const child of params.aggregatedChildren) {
child.setDataValue(params.column, params.newValue, 'data');
}
return true;
},
},
},
},
// other grid options ...
}Custom aggregation functions are disabled by default. To enable editing, add an explicit entry in the distribution record. The entry can be a strategy string, an options object, or a callback that implements the inverse of the aggregation logic:
const gridOptions = {
defaultColDef: {
editable: true,
groupRowEditable: true,
groupRowValueSetter: {
precision: 0,
distribution: {
sum: 'percentage',
avg: 'increment',
count: 'overwrite',
// Custom aggFunc: provide a callback that reverses the aggregation
myCustomAgg: (params) => {
for (const child of params.aggregatedChildren) {
child.setDataValue(params.column, params.newValue, 'data');
}
return true;
},
},
},
},
// other grid options ...
}Aggregation functions not listed in the record fall through to the default fallback, then to the built-in defaults (see the strategy table above).
Default Fallback Copy Link
When multiple custom aggregation functions share the same distribution logic, the default property avoids repeating entries for each one. It applies to any aggregation function not explicitly listed in the distribution record, including custom aggregations (which are otherwise disabled) and columns with no aggFunc (which already default to 'overwrite'). Non-distributable functions (count, min, max, first, last) are not affected by default and must always be listed explicitly.
const gridOptions = {
defaultColDef: {
editable: true,
groupRowEditable: true,
groupRowValueSetter: {
precision: 0,
distribution: {
sum: 'percentage',
count: 'overwrite',
},
// Fallback for unlisted custom aggFuncs and no-aggFunc columns.
default: 'overwrite',
},
},
// other grid options ...
} Custom getValue and setValue Copy Link
The getValue and setValue callbacks override how the built-in distributor reads from and writes to child rows. By default it uses node.getDataValue(column, 'value') to read and node.setDataValue(column, value, 'data') to write. Provide your own when child values live on a different field or custom data structure, when you need to apply transformations or side effects during distribution, or when the column's valueGetter / valueSetter are not suitable for distribution.
const gridOptions = {
columnDefs: [
{
field: 'amount',
aggFunc: 'sum',
editable: true,
groupRowEditable: true,
groupRowValueSetter: {
distribution: 'percentage',
getValue: (params) => (params.data && params.data.weight) || 0,
setValue: (params) => params.node.setDataValue(params.column, params.value, 'data'),
},
},
],
// other grid options ...
} Custom Distribution with a Callback Copy Link
For full control over how group-level edits cascade, assign a function to groupRowValueSetter. The callback receives a GroupRowValueSetterParams object and should return true when any child value was changed.
The example below distributes the edited total equally among children, rounded to integers (largest remainder method). It calls setDataValue on each child; aggregation then refreshes parent totals automatically.
GroupRowValueSetterParams Copy Link
Properties available on the GroupRowValueSetterParams<TData = any, TValue = any, TContext = any> interface.
The grid api. |
Application context as set on gridOptions.context. |
Column for this callback. |
ColDef provided for this column. |
The value before the change. |
The value after the change. |
The group row node being edited. |
Row data for the group node. null or undefined for grouping groups or tree data filler nodes. |
What triggered the edit (e.g. 'ui', 'undo', 'paste'). |
Whether the aggregated value actually changed compared to the previous value. |
The immediate children that contribute to this group's aggregation. setDataValue() on a child group cascades the edit recursively through the full hierarchy. The built-in distributeGroupValue does this automatically.rowNode.getAggregatedChildren(colKey) to retrieve the same children programmatically. Pass true as the second argument to collect all descendant leaf rows recursively. Only supported with the Client-Side Row Model.
|
Aggregated Children Copy Link
The groupRowValueSetter callback receives an aggregatedChildren array: the immediate children that contribute to the Aggregation for the edited column. Cascading edits to these children avoids traversing the row hierarchy by hand.
What aggregatedChildren returns depends on the column and grid configuration:
- Regular value columns: the direct children used for aggregation. This respects
suppressAggFilteredOnlyandgroupAggFiltering; when those cause aggregation to include all children rather than just filtered ones,aggregatedChildrenfollows suit. - Pivot columns on leaf groups: only the children matching the column's pivot keys, since pivot aggregation groups rows by pivot key values.
- Non-group rows: an empty array.
The same children can be retrieved programmatically with rowNode.getAggregatedChildren(colKey). To collect all descendant leaf rows instead of only the immediate children, pass true as the second argument: rowNode.getAggregatedChildren(colKey, true).
See Retrieving Aggregated Children for more details.
Cascading Edits Copy Link
Key points for implementing cascading edits:
- Call
rowNode.setDataValueon each child to write the new value down. - When a child is itself a group with its own
groupRowValueSetter, the cascade recurses further. - Parent aggregates refresh automatically: child
datachanges re-run the columnaggFunc, so editing a group total rebalances the children to match it. - Aggregation is batched. All
setDataValuecalls within a singlegroupRowValueSetterinvocation are processed before re-aggregation runs.
The aggregatedChildren parameter and rowNode.getAggregatedChildren() method are only supported with the Client-Side Row Model. For other row models, aggregatedChildren is an empty array.
Calling distributeGroupValue Copy Link
The built-in distribution function distributeGroupValue is exported from ag-grid-enterprise and can be called directly inside a custom groupRowValueSetter callback. This is useful when custom logic is needed before or after the distribution:
import { distributeGroupValue } from 'ag-grid-enterprise';
colDef.groupRowValueSetter = (params) => {
// Custom pre-processing
const adjusted = Math.max(0, Number(params.newValue));
return distributeGroupValue({ ...params, newValue: adjusted }, { distribution: 'percentage' });
};
You can also assign distributeGroupValue directly without a wrapper for default behaviour:
import { distributeGroupValue } from 'ag-grid-enterprise';
colDef.groupRowValueSetter = distributeGroupValue;
Editing Pivot Columns Copy Link
When using Pivoting, the groupRowValueSetter also works with pivot result columns. The key difference is that aggregatedChildren returns only the children matching the column's pivot keys, not all children of the group.
The following example uses the built-in distribution in pivot mode. Setting groupRowEditable enables distribution automatically. Double-click any pivot cell to edit it; the edited value is distributed uniformly among the children matching that pivot column's keys.
For full control, use a custom groupRowValueSetter callback. In the example below:
- The grid is pivoted by
product(Electronics, Clothing, Food), grouped byregionandcountry. - Double-clicking any pivot column cell opens the editor for the aggregated value.
- When editing a pivot cell (e.g. "Electronics" on the "Europe" row),
aggregatedChildrencontains only the European rows selling Electronics, not all European rows. - The edit is distributed equally among those matching children, leaving other product categories unchanged.
Edits on pivot columns therefore affect only the rows contributing to that pivot value, leaving other pivot categories untouched.
Tree Data Copy Link
groupRowEditable and groupRowValueSetter also work with Tree Data. Parent nodes act as group rows, so editing a parent's aggregated value cascades the change down to its children just as with row grouping.
The example below shows a company's budget organised as a tree: departments contain teams, and teams contain employees. Editing a department's or team's budget distributes the new total equally among its members, rounded to integers, recursing through the hierarchy.
Refreshing Groups After Editing Copy Link
When grouped columns are editable, setting refreshAfterGroupEdit=true causes the grid to update row data and recalculate the grouping after every committed edit. Without this option, the row data updates but the grouping does not get updated until the next full refresh.
When enabling refreshAfterGroupEdit, also provide getRowId so that the grid can track rows by stable IDs while rebuilding the grouping hierarchy.
The following example demonstrates this behaviour. Double-click on a department or team cell to edit it; the grid re-evaluates the grouping and moves the row to the correct group instantly.
See also Read Only Edit for configuring immutable grouped data or connecting the grid with a store.