---
title: "Aggregation - Total Rows"
enterprise: true
framework: javascript
version: "36.1.0"
---

# Aggregation - Total Rows

This section shows how to include group and grand total rows in the grid.

## Enabling a Grand Total Row

A grand total row can be included in the grid by setting the `grandTotalRow` grid option to one of: `"top"`, `"bottom"`, `"pinnedTop"` or `"pinnedBottom"`.

Setting a value of `"top"` or `"bottom"` renders the grand total row as the first or last row in the grid, respectively. Setting a value of `"pinnedTop"` or `"pinnedBottom"` renders the grand total row pinned to the top or bottom of the grid, respectively.

> **Note**
>
> Grand total rows are also supported with the [Server-Side Row Model](https://www.ag-grid.com/javascript-data-grid/server-side-model-grouping/#grand-total-row), including on flat grids without grouping.

#### Enabling Grand Total Row

```ts
import {
  ClientSideRowModelModule,
  GridApi,
  GridOptions,
  ModuleRegistry,
  PinnedRowModule,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";

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

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  RowGroupingModule,
  PinnedRowModule,
]);

let gridApi: GridApi;

const gridOptions: GridOptions<IOlympicData> = {
  columnDefs: [
    { field: "country", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ],
  defaultColDef: {
    flex: 1,
    minWidth: 150,
  },
  autoGroupColumnDef: {
    minWidth: 300,
  },
  grandTotalRow: "bottom",
};

function onChange() {
  const grandTotalRow = document.querySelector<HTMLInputElement>(
    "#input-property-value",
  )!.value;
  if (
    grandTotalRow === "bottom" ||
    grandTotalRow === "top" ||
    grandTotalRow === "pinnedTop" ||
    grandTotalRow === "pinnedBottom"
  ) {
    gridApi.setGridOption("grandTotalRow", grandTotalRow);
  } else {
    gridApi.setGridOption("grandTotalRow", undefined);
  }
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data: IOlympicData[]) => gridApi!.setGridOption("rowData", data));

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).onChange = onChange;
}
```

[Live example: Enabling Grand Total Row](https://www.ag-grid.com/examples/aggregation-total-rows/enabling-grand-total/typescript)

The following configuration shows how grand total rows can be included at the bottom of the grid:

```js
const gridOptions = {
    grandTotalRow: 'bottom',

    // other grid options ...
}
```

## Enabling Group Total Rows

A total row can be included in every group when using [Row Grouping](https://www.ag-grid.com/javascript-data-grid/grouping/) or [Tree Data](https://www.ag-grid.com/javascript-data-grid/tree-data/) by setting the `groupTotalRow` grid option to either `"top"` or `"bottom"`. The provided value determines whether the total row will be included as the first or last row in the group.

#### Enabling Group Total Row

```ts
import {
  ClientSideRowModelModule,
  GridApi,
  GridOptions,
  ModuleRegistry,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";

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

ModuleRegistry.registerModules([ClientSideRowModelModule, RowGroupingModule]);

let gridApi: GridApi;

const gridOptions: GridOptions<IOlympicData> = {
  columnDefs: [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ],
  defaultColDef: {
    flex: 1,
    minWidth: 150,
  },
  autoGroupColumnDef: {
    minWidth: 300,
  },
  groupDefaultExpanded: 1,
  groupTotalRow: "bottom",
};

function onChange() {
  const groupTotalRow = document.querySelector<HTMLInputElement>(
    "#input-property-value",
  )!.value;
  if (groupTotalRow === "bottom" || groupTotalRow === "top") {
    gridApi.setGridOption("groupTotalRow", groupTotalRow);
  } else {
    gridApi.setGridOption("groupTotalRow", undefined);
  }
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data: IOlympicData[]) => gridApi!.setGridOption("rowData", data));

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).onChange = onChange;
}
```

[Live example: Enabling Group Total Row](https://www.ag-grid.com/examples/aggregation-total-rows/enabling-group-total/typescript)

The following configuration shows how group total rows can be included at the bottom of every group:

```js
const gridOptions = {
    // adds subtotals to the bottom of each row group
    groupTotalRow: 'bottom',

    // other grid options ...
}
```

### Selectively Display Group Total Rows

Total rows can be applied to certain groups selectively by providing a callback to the `groupTotalRow` grid option. This callback should return `"top"`, `"bottom"` or `undefined` and will be called for each row group to determine whether the group should display a total row.

#### Selectively Enabling Group Footers

```ts
import {
  ClientSideRowModelModule,
  FirstDataRenderedEvent,
  GetGroupIncludeTotalRowParams,
  GridApi,
  GridOptions,
  ModuleRegistry,
  RowApiModule,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";

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

ModuleRegistry.registerModules([
  RowApiModule,
  ClientSideRowModelModule,
  RowGroupingModule,
]);

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ],
  defaultColDef: {
    flex: 1,
    minWidth: 150,
  },
  autoGroupColumnDef: {
    minWidth: 300,
  },
  groupTotalRow: (params: GetGroupIncludeTotalRowParams) => {
    const node = params.node;
    if (node && node.level === 1) return "bottom";
    if (node && node.key === "United States") return "bottom";

    return undefined;
  },
  onFirstDataRendered: (params: FirstDataRenderedEvent) => {
    params.api.forEachNode((node) => {
      if (node.key === "United States" || node.key === "Russia") {
        params.api.setRowNodeExpanded(node, true);
      }
    });
  },
};

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data: IOlympicData[]) =>
    gridApi!.setGridOption("rowData", data.slice(0, 50)),
  );
```

[Live example: Selectively Enabling Group Footers](https://www.ag-grid.com/examples/aggregation-total-rows/enabling-group-total-selectively/typescript)

The example above demonstrates the following configuration to display total rows for the `"United States"` group, and the rows grouped by the `"year"` field:

```js
const gridOptions = {
    groupTotalRow: (params) => {
        const node = params.node;
        if (node && node.level === 1) return 'bottom';
        if (node && node.key === 'United States') return 'bottom';
        return undefined;
    },

    // other grid options ...
}
```

### Keeping Group Row Values

When a total row is visible, the group row values are hidden. This behaviour can be prevented by setting the `groupSuppressBlankHeader` grid option to `true`.

#### Suppress Blank Groups

```ts
import {
  ClientSideRowModelModule,
  GridApi,
  GridOptions,
  ModuleRegistry,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";

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

ModuleRegistry.registerModules([ClientSideRowModelModule, RowGroupingModule]);

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ],
  defaultColDef: {
    flex: 1,
    minWidth: 150,
  },
  autoGroupColumnDef: {
    minWidth: 300,
  },
  groupTotalRow: "bottom",
  groupDefaultExpanded: 1,
};

function toggleProperty() {
  const enable = document.querySelector<HTMLInputElement>(
    "#groupSuppressBlankHeader",
  )!.checked;
  gridApi.setGridOption("groupSuppressBlankHeader", enable);
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data: IOlympicData[]) => gridApi!.setGridOption("rowData", data));

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).toggleProperty = toggleProperty;
}
```

[Live example: Suppress Blank Groups](https://www.ag-grid.com/examples/aggregation-total-rows/suppress-blank-groups/typescript)

The configuration below demonstrates the configuration for preventing the hiding of group row values:

```js
const gridOptions = {
    groupSuppressBlankHeader: true,

    // other grid options ...
}
```

## Group Column Cell Values

When using [Row Grouping](https://www.ag-grid.com/javascript-data-grid/grouping-display-types/) or [Tree Data](https://www.ag-grid.com/javascript-data-grid/tree-data-group-column/) with group columns, the group cell will display `"Total"` by default in the footer rows.

The default `agGroupCellRenderer.cellRendererParams` can be provided with a `totalValueGetter` to configure the value displayed in this cell.

#### Customising Footer Values

```ts
import {
  ClientSideRowModelModule,
  GridApi,
  GridOptions,
  ModuleRegistry,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";

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

ModuleRegistry.registerModules([ClientSideRowModelModule, RowGroupingModule]);

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ],
  defaultColDef: {
    flex: 1,
    minWidth: 150,
  },
  autoGroupColumnDef: {
    minWidth: 300,
    cellRendererParams: {
      totalValueGetter: (params: any) => {
        const isRootLevel = params.node.level === -1;
        if (isRootLevel) {
          return "Grand Total";
        }
        return `Sub Total (${params.value})`;
      },
    },
  },
  groupTotalRow: "bottom",
  grandTotalRow: "bottom",
};

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data: IOlympicData[]) => gridApi!.setGridOption("rowData", data));
```

[Live example: Customising Footer Values](https://www.ag-grid.com/examples/aggregation-total-rows/customising-footer-values/typescript)

The example above demonstrates using the following configuration to display custom group column values for grand total and group total rows:

```js
const gridOptions = {
    autoGroupColumnDef: {
        cellRendererParams: {
            totalValueGetter: params =>  {
                const isRootLevel = params.node.level === -1;
                if (isRootLevel) {
                    return 'Grand Total';
                }
                return `Sub Total (${params.value})`;
            },
        }
    },

    // other grid options ...
}
```

> **Note**
>
> When exporting, copying custom footers, or using Find with custom group cell values, the custom content must also be added using [processRowGroupCallback](https://www.ag-grid.com/javascript-data-grid/excel-export-customising-content/) for export, [processCellForClipboard](https://www.ag-grid.com/javascript-data-grid/clipboard/#processing-individual-cells) for copying to clipboard, or [getFindText](https://www.ag-grid.com/javascript-data-grid/find/#using-find-with-cell-components) for Find.

## Suppress Sticky Rows

All total rows stick to the top or bottom of the viewport when scrolling. This behaviour can be configured by using the `suppressStickyTotalRow` grid option.

#### Suppress Sticky Total Rows

```ts
import {
  ClientSideRowModelModule,
  GridApi,
  GridOptions,
  ModuleRegistry,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";

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

ModuleRegistry.registerModules([ClientSideRowModelModule, RowGroupingModule]);

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: [
    { field: "country", rowGroup: true, hide: true },
    { field: "year", rowGroup: true, hide: true },
    { field: "gold", aggFunc: "sum" },
    { field: "silver", aggFunc: "sum" },
    { field: "bronze", aggFunc: "sum" },
  ],
  defaultColDef: {
    flex: 1,
    minWidth: 150,
  },
  autoGroupColumnDef: {
    minWidth: 300,
  },
  groupDefaultExpanded: -1,
  groupTotalRow: "bottom",
  grandTotalRow: "bottom",
};

function onChange() {
  const suppressStickyTotalRow = document.querySelector<HTMLInputElement>(
    "#input-property-value",
  )!.value;
  if (
    suppressStickyTotalRow === "grand" ||
    suppressStickyTotalRow === "group"
  ) {
    gridApi.setGridOption("suppressStickyTotalRow", suppressStickyTotalRow);
  } else if (suppressStickyTotalRow === "true") {
    gridApi.setGridOption("suppressStickyTotalRow", true);
  } else {
    gridApi.setGridOption("suppressStickyTotalRow", false);
  }
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data: IOlympicData[]) => gridApi!.setGridOption("rowData", data));

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).onChange = onChange;
}
```

[Live example: Suppress Sticky Total Rows](https://www.ag-grid.com/examples/aggregation-total-rows/suppress-sticky-total-rows/typescript)

The following configuration demonstrates how to suppress sticky behaviour for both grand and group total rows:

```js
const gridOptions = {
    suppressStickyTotalRow: true,

    // other grid options ...
}
```
