---
product: "AG Grid"
title: "Row Grouping - Sorting"
description: "This section provides details on how to configure and customise how row groups are sorted."
enterprise: true
framework: angular
version: "36.2.0"
related:
    - title: "Overview"
      url: "https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping/"
    - title: "Grouping Data"
      url: "https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-data/"
    - title: "Group Display Types"
      url: "https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-display-types/"
    - title: "Row Group Panel"
      url: "https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-group-panel/"
    - title: "Expanding Groups"
      url: "https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-opening-groups/"
    - title: "Hierarchy Selection"
      url: "https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-row-selection/"
    - title: "Editing Groups"
      url: "https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-edit/"
    - title: "Row Dragging"
      url: "https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-row-dragging/"
llms: "https://www.ag-grid.com/archive/36.2.0/llms.txt"
---

# Row Grouping - Sorting

This section provides details on how to configure and customise how row groups are sorted.

## Sorting Row Groups

Row Groups are [Sorted](https://www.ag-grid.com/archive/36.2.0/angular-data-grid/row-sorting/) by the column that they are grouped by, and use any [Custom Sorting](https://www.ag-grid.com/archive/36.2.0/angular-data-grid/row-sorting/#custom-sorting) configured on that column. Applying a sort to a group column generated by `groupDisplayType` will apply the sort to the row grouped columns it represents.

#### Mixed Group Sort

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
} from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [groupDefaultExpanded]="groupDefaultExpanded"
    [rowData]="rowData"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, sort: "desc" },
    { field: "year", rowGroup: true, sort: "asc" },
    { field: "athlete" },
    { field: "total" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 100,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 300,
  };
  groupDefaultExpanded = 1;
  rowData!: any[];

  constructor(private http: HttpClient) {}

  onGridReady(params: GridReadyEvent) {
    this.http
      .get<any[]>("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Mixed Group Sort](https://www.ag-grid.com/archive/36.2.0/examples/grouping-sorting/mixed-group-sort/angular/)

The example above demonstrates that sorting the `country` and `year` columns will sort the row groups, and clicking to sort the `Group` column applies sorting to the `country` and `year` columns.

> **Note**
>
> When using `groupDisplayType` with a [Single Group Column](https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-single-group-column/) and the columns with row grouping applied have different sort directions, the group column will instead display the mixed sort icon.

```ts
<ag-grid-angular
    [columnDefs]="columnDefs"
    [groupDisplayType]="groupDisplayType"
    /* other grid options ... */ />

this.columnDefs = [
    { field: 'country', rowGroup: true, sort: 'desc' },
    { field: 'year', rowGroup: true, sort: 'asc' },
    // ...other column definitions
];
this.groupDisplayType = 'singleColumn';
```

## Custom Row Group Sorting

The generated Group Columns can be unlinked from the columns with row grouping by configuring [Custom Group Sorting](https://www.ag-grid.com/archive/36.2.0/angular-data-grid/row-sorting/#custom-sorting) using a `autoGroupColumnDef.comparator`. This allows custom sorting to be applied across all levels of row grouping.

The example below demonstrates a configuration that ignores the data entirely, sorting rows by the number of descendants instead:

```ts
<ag-grid-angular
    [columnDefs]="columnDefs"
    [autoGroupColumnDef]="autoGroupColumnDef"
    /* other grid options ... */ />

this.columnDefs = [
    { field: 'country', rowGroup: true },
    { field: 'year', rowGroup: true },
    // ...other column definitions
];
this.autoGroupColumnDef = {
    comparator: (valueA, valueB, nodeA, nodeB) => {
        return nodeA.allLeafChildren.length - nodeB.allLeafChildren.length;
    },
};
```

#### Custom Group Sort

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
} from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [groupDefaultExpanded]="groupDefaultExpanded"
    [rowData]="rowData"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true },
    { field: "year", rowGroup: true },
    { field: "athlete" },
    { field: "total" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 100,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 300,
    comparator: (valueA, valueB, nodeA, nodeB) =>
      (nodeA.allLeafChildren?.length ?? 0) -
      (nodeB.allLeafChildren?.length ?? 0),
  };
  groupDefaultExpanded = 1;
  rowData!: any[];

  constructor(private http: HttpClient) {}

  onGridReady(params: GridReadyEvent) {
    this.http
      .get<any[]>("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Custom Group Sort](https://www.ag-grid.com/archive/36.2.0/examples/grouping-sorting/custom-group-sort/angular/)

> **Note**
>
> When using custom group sorting, sorting the `Group` column no longer impacts the columns with row grouping, and vice versa.

## Maintain Group Order

By default, sorting on a non-group column reorders groups based on the sort. To keep groups in their structural order while only sorting the rows within each group, enable `groupMaintainOrder`:

```ts
<ag-grid-angular
    [columnDefs]="columnDefs"
    [groupMaintainOrder]="groupMaintainOrder"
    /* other grid options ... */ />

this.columnDefs = [
    { field: 'country', rowGroup: true, hide: true },
    { field: 'athlete' },
];
this.groupMaintainOrder = true;
```

With `groupMaintainOrder=true`:

- Sorting a leaf column sorts the rows inside each group; groups stay in structural order.
- Filter changes preserve group order.
- Transactions add new groups at their structural position: the end of the data-insertion order, or the position produced by `initialGroupOrderComparator` if one is configured.
- With multi-level row grouping, the order is maintained per level. Sorting a group column at one level only re-orders that level's groups; sibling levels keep their structural order. For example, with `country` and `year` grouping, sorting `year` re-orders year groups within each country, while country groups remain in their structural slot.

#### Maintain Group Order

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  RowGroupingDisplayType,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
} from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [groupDisplayType]="groupDisplayType"
    [groupMaintainOrder]="true"
    [groupDefaultExpanded]="groupDefaultExpanded"
    [rowData]="rowData"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "athlete" },
    { field: "total" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 100,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 200,
  };
  groupDisplayType: RowGroupingDisplayType = "multipleColumns";
  groupDefaultExpanded = 1;
  rowData!: any[];

  constructor(private http: HttpClient) {}

  onGridReady(params: GridReadyEvent) {
    this.http
      .get<any[]>("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Maintain Group Order](https://www.ag-grid.com/archive/36.2.0/examples/grouping-sorting/maintain-group-order/angular/)

The example above uses `groupDisplayType: 'multipleColumns'` so each group level has its own header. Try the following to see per-level isolation:

- Sort `Athlete` or `Total` (leaf columns): only the rows inside each year group reorder; year and country groups keep their structural order.
- Sort the `Year` group column: only year groups within each country reorder; country groups stay structural.
- Sort the `Country` group column: only country groups reorder; year groups within each country keep their structural order.
- Clear a group-column sort: that level reverts to structural order; sibling levels are unaffected.

The structural order is the data-insertion order by default, or the order produced by [`initialGroupOrderComparator`](https://www.ag-grid.com/archive/36.2.0/angular-data-grid/grouping-sorting/#unsorted-group-order) if one is configured. If a group column had a sort applied and the user later explicitly clears that sort, the structural order is restored.

## Unsorted Group Order

When no sorting is applied, the groups are ordered by the order in which they appear in the data. This order can be overwritten with a custom initial order by providing an `initialGroupOrderComparator` grid option.

> **Note**
>
> As this is an initial order of groups, it executes before filtering and aggregation. This means it cannot use post-filtered data, or aggregated values as comparison criteria.

#### Initial Group Order

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import {
  AutoGroupColumnDef,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  InitialGroupOrderComparator,
  InitialGroupOrderComparatorParams,
  ModuleRegistry,
  RowGroupingDisplayType,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
} from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [autoGroupColumnDef]="autoGroupColumnDef"
    [groupDisplayType]="groupDisplayType"
    [initialGroupOrderComparator]="initialGroupOrderComparator"
    [rowData]="rowData"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "athlete" },
    { field: "total" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 100,
  };
  autoGroupColumnDef: AutoGroupColumnDef = {
    minWidth: 200,
  };
  groupDisplayType: RowGroupingDisplayType = "multipleColumns";
  initialGroupOrderComparator: InitialGroupOrderComparator = (
    params: InitialGroupOrderComparatorParams,
  ) =>
    params.nodeA.allLeafChildren.length - params.nodeB.allLeafChildren.length;
  rowData!: any[];

  constructor(private http: HttpClient) {}

  onGridReady(params: GridReadyEvent) {
    this.http
      .get<any[]>("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Initial Group Order](https://www.ag-grid.com/archive/36.2.0/examples/grouping-sorting/initial-group-order/angular/)

The example above demonstrates the following configuration to order group rows based on the number of leaf children:

```ts
<ag-grid-angular
    [initialGroupOrderComparator]="initialGroupOrderComparator"
    /* other grid options ... */ />

this.initialGroupOrderComparator = (params) =>
    params.nodeA.allLeafChildren.length - params.nodeB.allLeafChildren.length;
```
