This section shows how to include group and grand total rows in the grid.
Enabling a Grand Total Row Copy Link
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.
Grand total rows are also supported with the Server-Side Row Model, including on flat grids without grouping.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./styles.css";
import {
AutoGroupColumnDef,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
PinnedRowModule,
enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [ClientSideRowModelModule, RowGroupingModule, PinnedRowModule];
const GridExample = () => {
const gridRef = useRef<AgGridReact<IOlympicData>>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "country", rowGroup: true, hide: true },
{ field: "gold", aggFunc: "sum" },
{ field: "silver", aggFunc: "sum" },
{ field: "bronze", aggFunc: "sum" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 150,
};
}, []);
const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
return {
minWidth: 300,
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
const onChange = useCallback(() => {
const grandTotalRow = document.querySelector<HTMLInputElement>(
"#input-property-value",
)!.value;
if (
grandTotalRow === "bottom" ||
grandTotalRow === "top" ||
grandTotalRow === "pinnedTop" ||
grandTotalRow === "pinnedBottom"
) {
gridRef.current!.api.setGridOption("grandTotalRow", grandTotalRow);
} else {
gridRef.current!.api.setGridOption("grandTotalRow", undefined);
}
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="example-wrapper">
<div className="example-header">
<label>
<span>grandTotalRow:</span>
<select id="input-property-value" onChange={onChange}>
<option value="bottom">"bottom"</option>
<option value="top">"top"</option>
<option value="pinnedBottom">"pinnedBottom"</option>
<option value="pinnedTop">"pinnedTop"</option>
<option value="undefined">undefined</option>
</select>
</label>
</div>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
ref={gridRef}
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
autoGroupColumnDef={autoGroupColumnDef}
grandTotalRow={"bottom"}
/>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
.example-header {
margin-bottom: 5px;
}
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; The following configuration shows how grand total rows can be included at the bottom of the grid:
const grandTotalRow = 'bottom';
<AgGridReact grandTotalRow={grandTotalRow} /> Enabling Group Total Rows Copy Link
A total row can be included in every group when using Row Grouping or 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.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./styles.css";
import {
AutoGroupColumnDef,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
UseGroupTotalRow,
enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [ClientSideRowModelModule, RowGroupingModule];
const GridExample = () => {
const gridRef = useRef<AgGridReact<IOlympicData>>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "country", rowGroup: true, hide: true },
{ field: "year", rowGroup: true, hide: true },
{ field: "gold", aggFunc: "sum" },
{ field: "silver", aggFunc: "sum" },
{ field: "bronze", aggFunc: "sum" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 150,
};
}, []);
const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
return {
minWidth: 300,
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
const onChange = useCallback(() => {
const groupTotalRow = document.querySelector<HTMLInputElement>(
"#input-property-value",
)!.value;
if (groupTotalRow === "bottom" || groupTotalRow === "top") {
gridRef.current!.api.setGridOption("groupTotalRow", groupTotalRow);
} else {
gridRef.current!.api.setGridOption("groupTotalRow", undefined);
}
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="example-wrapper">
<div className="example-header">
<label>
<span>groupTotalRow:</span>
<select id="input-property-value" onChange={onChange}>
<option value="bottom">"bottom"</option>
<option value="top">"top"</option>
<option value="undefined">undefined</option>
</select>
</label>
</div>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
ref={gridRef}
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
autoGroupColumnDef={autoGroupColumnDef}
groupDefaultExpanded={1}
groupTotalRow={"bottom"}
/>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
.example-header {
margin-bottom: 5px;
}
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; The following configuration shows how group total rows can be included at the bottom of every group:
// adds subtotals to the bottom of each row group
const groupTotalRow = 'bottom';
<AgGridReact groupTotalRow={groupTotalRow} /> Selectively Display Group Total Rows Copy Link
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.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
AutoGroupColumnDef,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
FirstDataRenderedEvent,
GetGroupIncludeTotalRowParams,
GridApi,
GridOptions,
ModuleRegistry,
RowApiModule,
UseGroupTotalRow,
enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [RowApiModule, ClientSideRowModelModule, RowGroupingModule];
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "country", rowGroup: true, hide: true },
{ field: "year", rowGroup: true, hide: true },
{ field: "gold", aggFunc: "sum" },
{ field: "silver", aggFunc: "sum" },
{ field: "bronze", aggFunc: "sum" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 150,
};
}, []);
const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
return {
minWidth: 300,
};
}, []);
const groupTotalRow = useCallback((params: GetGroupIncludeTotalRowParams) => {
const node = params.node;
if (node && node.level === 1) return "bottom";
if (node && node.key === "United States") return "bottom";
return undefined;
}, []);
const { data, loading } = useFetchJson<any>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
50,
);
const onFirstDataRendered = useCallback((params: FirstDataRenderedEvent) => {
params.api.forEachNode((node) => {
if (node.key === "United States" || node.key === "Russia") {
params.api.setRowNodeExpanded(node, true);
}
});
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div style={gridStyle}>
<AgGridReact
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
autoGroupColumnDef={autoGroupColumnDef}
groupTotalRow={groupTotalRow}
onFirstDataRendered={onFirstDataRendered}
/>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; The example above demonstrates the following configuration to display total rows for the "United States" group, and the rows grouped by the "year" field:
const groupTotalRow = (params) => {
const node = params.node;
if (node && node.level === 1) return 'bottom';
if (node && node.key === 'United States') return 'bottom';
return undefined;
};
<AgGridReact groupTotalRow={groupTotalRow} /> Keeping Group Row Values Copy Link
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.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./styles.css";
import {
AutoGroupColumnDef,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
UseGroupTotalRow,
enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [ClientSideRowModelModule, RowGroupingModule];
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "country", rowGroup: true, hide: true },
{ field: "year", rowGroup: true, hide: true },
{ field: "gold", aggFunc: "sum" },
{ field: "silver", aggFunc: "sum" },
{ field: "bronze", aggFunc: "sum" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 150,
};
}, []);
const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
return {
minWidth: 300,
};
}, []);
const { data, loading } = useFetchJson<any>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
const toggleProperty = useCallback(() => {
const enable = document.querySelector<HTMLInputElement>(
"#groupSuppressBlankHeader",
)!.checked;
gridRef.current!.api.setGridOption("groupSuppressBlankHeader", enable);
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="example-wrapper">
<div className="example-header">
<label>
<span>groupSuppressBlankHeader:</span>
<input
id="groupSuppressBlankHeader"
type="checkbox"
onChange={toggleProperty}
/>
</label>
</div>
<div style={gridStyle}>
<AgGridReact
ref={gridRef}
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
autoGroupColumnDef={autoGroupColumnDef}
groupTotalRow={"bottom"}
groupDefaultExpanded={1}
/>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
.example-header {
margin-bottom: 10px;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; The configuration below demonstrates the configuration for preventing the hiding of group row values:
const groupSuppressBlankHeader = true;
<AgGridReact groupSuppressBlankHeader={groupSuppressBlankHeader} /> Group Column Cell Values Copy Link
When using Row Grouping or Tree Data 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.
The example above demonstrates using the following configuration to display custom group column values for grand total and group total rows:
const autoGroupColumnDef = useMemo(() => {
return {
cellRendererParams: {
totalValueGetter: params => {
const isRootLevel = params.node.level === -1;
if (isRootLevel) {
return 'Grand Total';
}
return `Sub Total (${params.value})`;
},
}
};
}, []);
<AgGridReact autoGroupColumnDef={autoGroupColumnDef} />When exporting, copying custom footers, or using Find with custom group cell values, the custom content must also be added using processRowGroupCallback for export, processCellForClipboard for copying to clipboard, or getFindText for Find.
Suppress Sticky Rows Copy Link
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.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./styles.css";
import {
AutoGroupColumnDef,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
UseGroupTotalRow,
enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [ClientSideRowModelModule, RowGroupingModule];
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "country", rowGroup: true, hide: true },
{ field: "year", rowGroup: true, hide: true },
{ field: "gold", aggFunc: "sum" },
{ field: "silver", aggFunc: "sum" },
{ field: "bronze", aggFunc: "sum" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 150,
};
}, []);
const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
return {
minWidth: 300,
};
}, []);
const { data, loading } = useFetchJson<any>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
const onChange = useCallback(() => {
const suppressStickyTotalRow = document.querySelector<HTMLInputElement>(
"#input-property-value",
)!.value;
if (
suppressStickyTotalRow === "grand" ||
suppressStickyTotalRow === "group"
) {
gridRef.current!.api.setGridOption(
"suppressStickyTotalRow",
suppressStickyTotalRow,
);
} else if (suppressStickyTotalRow === "true") {
gridRef.current!.api.setGridOption("suppressStickyTotalRow", true);
} else {
gridRef.current!.api.setGridOption("suppressStickyTotalRow", false);
}
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="example-wrapper">
<div className="example-header">
<label>
<span>suppressStickyTotalRow:</span>
<select id="input-property-value" onChange={onChange}>
<option value="false">false</option>
<option value="true">true</option>
<option value="grand">"grand"</option>
<option value="group">"group"</option>
</select>
</label>
</div>
<div style={gridStyle}>
<AgGridReact
ref={gridRef}
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
autoGroupColumnDef={autoGroupColumnDef}
groupDefaultExpanded={-1}
groupTotalRow={"bottom"}
grandTotalRow={"bottom"}
/>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
.example-header {
margin-bottom: 5px;
}
import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; The following configuration demonstrates how to suppress sticky behaviour for both grand and group total rows:
const suppressStickyTotalRow = true;
<AgGridReact suppressStickyTotalRow={suppressStickyTotalRow} />