---
product: "AG Grid"
title: "Row Grouping - Grouping Data"
description: "Enable row grouping in the React Data Grid to allow rows to be grouped by columns. Define row groups, use the Grid API, or use the UI to group rows."
enterprise: true
framework: react
version: "36.2.0"
related:
    - title: "Overview"
      url: "https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping/"
    - title: "Group Display Types"
      url: "https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping-display-types/"
    - title: "Row Group Panel"
      url: "https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping-group-panel/"
    - title: "Expanding Groups"
      url: "https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping-opening-groups/"
    - title: "Hierarchy Selection"
      url: "https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping-row-selection/"
    - title: "Sorting"
      url: "https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping-sorting/"
    - title: "Editing Groups"
      url: "https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping-edit/"
    - title: "Row Dragging"
      url: "https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping-row-dragging/"
llms: "https://www.ag-grid.com/archive/36.2.0/llms.txt"
---

# Row Grouping - Grouping Data

Enable grouping on a column to group rows by equivalent values.

## Enabling Row Grouping

Row Grouping is enabled by setting `rowGroup` to `true` on one or more [Column Definition](https://www.ag-grid.com/archive/36.2.0/react-data-grid/column-definitions/). Parent rows are then introduced for each unique value in that column, containing the rows with that value.

#### Basic Grouping

```tsx
"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,
  GridApi,
  GridOptions,
  ModuleRegistry,
  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 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" },
    { field: "athlete" },
    { field: "sport" },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      minWidth: 200,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Basic Grouping](https://www.ag-grid.com/archive/36.2.0/examples/grouping-data/basic-grouping/reactFunctionalTs/)

The example above uses the following configuration to group rows by their `country` values:

```jsx
const [columnDefs, setColumnDefs] = useState([
    { field: 'country', rowGroup: true },
    // ...other column definitions
]);

<AgGridReact columnDefs={columnDefs} />
```

## Grouping by Multiple Columns

When grouping on multiple columns using `rowGroup`, the order of columns within the column definitions is used to determine which column to group by first. This can be overridden with a custom order by providing the `rowGroupIndex` property in each grouped columns definition.

#### Grouping by Multiple Columns

```tsx
"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,
  GridApi,
  GridOptions,
  ModuleRegistry,
  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 containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "country", rowGroupIndex: 1, hide: true },
    { field: "year", rowGroupIndex: 0, hide: true },
    { field: "athlete" },
    { field: "sport" },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      minWidth: 200,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
            groupDefaultExpanded={1}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Grouping by Multiple Columns](https://www.ag-grid.com/archive/36.2.0/examples/grouping-data/row-group-index/reactFunctionalTs/)

The example above demonstrates the following configuration for grouping rows by `year` first, and `country` second:

```jsx
const [columnDefs, setColumnDefs] = useState([
    { field: 'country', rowGroupIndex: 1 },
    { field: 'year', rowGroupIndex: 0 },
    // ...other column definitions
]);

<AgGridReact columnDefs={columnDefs} />
```

## Grouping on Object Data

When grouping on object data, the grid needs a way to compare items to determine if they are equivalent. Setting a `keyCreator` on the grouped column definition provides the grid with string keys it can compare.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `keyCreator` | `KeyCreatorFunc` |  |  |  |

The following example uses a custom set of rows, each containing an `athlete` field that maps to objects with `id` and `name` properties.

#### Grouping by Object Data

```tsx
"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,
  GridApi,
  GridOptions,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { RowGroupingModule } from "ag-grid-enterprise";
import { getData } from "./data";

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

const modules = [ClientSideRowModelModule, RowGroupingModule];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<any[]>(getData());
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    {
      field: "athlete",
      rowGroup: true,
      hide: true,
      // `value` can be `null`/`undefined` for group and footer rows, which have no athlete object.
      keyCreator: (params) => params.value?.id ?? "",
      valueFormatter: (params) => params.value?.name ?? "",
    },
    { field: "country" },
    { field: "year" },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      minWidth: 200,
    };
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Grouping by Object Data](https://www.ag-grid.com/archive/36.2.0/examples/grouping-data/grouping-object-data/reactFunctionalTs/)

This demonstrates the following configuration for grouping rows by the `athlete` objects by their `id` property:

```jsx
const [columnDefs, setColumnDefs] = useState([
    {
        field: 'athlete',
        rowGroup: true,
        // params.value is undefined for group rows, so check before reading `id`/`name`
        keyCreator: (params) => params.value?.id ?? '',
        valueFormatter: (params) => params.value?.name ?? '',
    },
    // ...other column definitions
]);

<AgGridReact columnDefs={columnDefs} />
```

`keyCreator` and `valueFormatter` are called for group rows as well as leaf rows. A group row has no row `data` of its own, so `params.value` is `undefined` there even for the grouped column itself. See [TypeScript Generics](https://www.ag-grid.com/archive/36.2.0/react-data-grid/typescript-generics/) for how typing these callbacks' params surfaces this case at compile time.

## Grouping by Dates and Times

When grouping by date/time values, the grid can optionally group by components of the date/time.

To enable grouping by parts of the date for a particular column, use the `groupHierarchy` property of the [Column Definition](https://www.ag-grid.com/archive/36.2.0/react-data-grid/column-properties/#reference-grouping-groupHierarchy).

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `groupHierarchy` | `(GroupHierarchyParts \| string \| ColDef)[]` |  |  |  |

```jsx
const [columnDefs, setColumnDefs] = useState([
    {
        field: 'date',
        rowGroup: true,
        groupHierarchy: ['year', 'month']
    },
    // ...other column definitions
]);

<AgGridReact columnDefs={columnDefs} />
```

This snippet is illustrated in the example below.

#### Grouping by Dates and Times

```tsx
"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,
  GridReadyEvent,
  ModuleRegistry,
  SideBarDef,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnsToolPanelModule,
  PivotModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  SideBarModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";

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

const modules = [
  ClientSideRowModelModule,
  RowGroupingModule,
  SideBarModule,
  ColumnsToolPanelModule,
  RowGroupingPanelModule,
  PivotModule,
];

const COL_DEFS: ColDef<IOlympicData>[] = [
  {
    field: "date",
    rowGroup: true,
    enableRowGroup: true,
    enablePivot: true,
    groupHierarchy: ["year", "month"],
    minWidth: 120,
  },
  { field: "country" },
  { field: "sport" },
  { field: "total", aggFunc: "sum" },
];

const GridExample = () => {
  const gridRef = useRef<AgGridReact<IOlympicData>>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<IOlympicData[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>(COL_DEFS);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      minWidth: 225,
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/small-olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicData[]) =>
        setRowData(
          data.map((d) => ({
            ...d,
            date: d.date?.split("/").reverse().join("-"),
          })),
        ),
      );
  }, []);

  const onChangeFormattedMonth = useCallback((event: any) => {
    const month = event.target.checked ? "formattedMonth" : "month";
    COL_DEFS[0].groupHierarchy![1] = month;
    gridRef.current!.api.setGridOption("columnDefs", COL_DEFS);
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <label style={{ marginBottom: "1rem" }}>
            <input
              id="formatted-month-checkbox"
              type="checkbox"
              onChange={() => onChangeFormattedMonth(event)}
            />
            <span>Use formatted month</span>
          </label>

          <div style={gridStyle}>
            <AgGridReact<IOlympicData>
              ref={gridRef}
              rowData={rowData}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              autoGroupColumnDef={autoGroupColumnDef}
              sideBar={"columns"}
              rowGroupPanelShow={"always"}
              onGridReady={onGridReady}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Grouping by Dates and Times](https://www.ag-grid.com/archive/36.2.0/examples/grouping-data/grouping-date-time/reactFunctionalTs/)

> **Note**
>
> By default, the grid requires datetime values to be formatted using the ISO-8601 format in order to be correctly parsed into their components.
>
> To use other date formats, provide a custom [Cell Data Type Definition](https://www.ag-grid.com/archive/36.2.0/react-data-grid/cell-data-types/#providing-custom-cell-data-types) for the `dateString` and/or `dateTimeString` data types.

## Defining Custom Grouping Hierarchies

When using `groupHierarchy` as demonstrated in [Grouping by Dates and Times](https://www.ag-grid.com/archive/36.2.0/react-data-grid/grouping-data/#grouping-by-dates-and-times), the grid provides built-in support for several components of a date/time value.

Users may instead provide definitions of their own components via the `groupHierarchyConfig` grid option. These definitions may then be used in the `groupHierarchy` property of a column definition.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `groupHierarchyConfig` | `GroupHierarchyConfig` |  |  |  |

The following example illustrates this by defining a custom grouping hierarchy component that allows grouping by the week number:

#### Grouping by Dates and Times

```tsx
"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,
  ColumnApiModule,
  GridApi,
  GridOptions,
  GridReadyEvent,
  GroupHierarchyConfig,
  ModuleRegistry,
  SideBarDef,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnsToolPanelModule,
  PivotModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  SideBarModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";

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

const modules = [
  ClientSideRowModelModule,
  RowGroupingModule,
  SideBarModule,
  ColumnsToolPanelModule,
  RowGroupingPanelModule,
  ColumnApiModule,
  PivotModule,
];

const getDate: (value: any) => Date | null = (value: any) => {
  if (value instanceof Date) {
    return value;
  }
  if (typeof value === "string") {
    const [year, month, day] = value.split("-");
    const d = new Date();
    d.setFullYear(parseInt(year, 10), parseInt(month, 10), parseInt(day, 10));
    d.setHours(0, 0, 0, 0);
    return d;
  }
  return null;
};

const getWeekNumber: (date: Date) => number = (date: Date) => {
  const d = new Date(date.getTime());
  d.setHours(0, 0, 0, 0);
  d.setDate(d.getDate() + 3 - ((date.getDay() + 6) % 7));
  const week1 = new Date(d.getFullYear(), 0, 4);
  return (
    1 +
    Math.round(
      ((d.getTime() - week1.getTime()) / 86400000 -
        3 +
        ((week1.getDay() + 6) % 7)) /
        7,
    )
  );
};

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<IOlympicData[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    {
      field: "date",
      rowGroup: true,
      enableRowGroup: true,
      enablePivot: true,
      groupHierarchy: ["year", "week"],
      minWidth: 120,
    },
    { field: "country" },
    { field: "sport" },
    { field: "total", aggFunc: "sum" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      minWidth: 225,
    };
  }, []);
  const groupHierarchyConfig = useMemo<GroupHierarchyConfig>(() => {
    return {
      week: {
        headerValueGetter: (params) => {
          const sourceCol = params.api
            .getColumns()
            ?.find((col) => col.getColDef().field === "date");
          if (!sourceCol) return "";
          const name = params.api.getDisplayNameForColumn(
            sourceCol,
            params.location,
          );
          return `${name} (Week)`;
        },
        valueGetter: (params) => {
          const sourceCol = params.api
            .getColumns()
            ?.find((col) => col.getColDef().field === "date");
          const field = sourceCol?.getColDef().field;
          if (!field) return;
          const value = params.getValue(field);
          const date = getDate(value);
          if (!date) return;
          return getWeekNumber(date).toString();
        },
      },
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/small-olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicData[]) =>
        setRowData(
          data.map((d) => ({
            ...d,
            date: d.date?.split("/").reverse().join("-"),
          })),
        ),
      );
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
            sideBar={"columns"}
            rowGroupPanelShow={"always"}
            groupHierarchyConfig={groupHierarchyConfig}
            onGridReady={onGridReady}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Grouping by Dates and Times](https://www.ag-grid.com/archive/36.2.0/examples/grouping-data/grouping-custom-date-time/reactFunctionalTs/)

## Grouping on Null and Undefined Data

When grouping `null`, `undefined` or `""` (empty string) values the grid will group these together under the heading `(Blanks)` as the final group.

By setting the `groupAllowUnbalanced` property to `true`, the grid will instead display these rows without a group.

#### Grouping by Null and Undefined Data

```tsx
"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,
  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" },
    { field: "athlete" },
    { field: "sport" },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      minWidth: 200,
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/small-olympic-winners.json",
  );

  const toggleGroupAllowUnbalanced = useCallback(() => {
    const enable = document.querySelector<HTMLInputElement>(
      "#groupAllowUnbalanced",
    )!.checked;
    gridRef.current!.api.setGridOption("groupAllowUnbalanced", enable);
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div className="example-header">
            <label>
              <span>groupAllowUnbalanced:</span>
              <input
                id="groupAllowUnbalanced"
                type="checkbox"
                onChange={toggleGroupAllowUnbalanced}
              />
            </label>
          </div>

          <div style={gridStyle}>
            <AgGridReact<IOlympicData>
              ref={gridRef}
              rowData={data}
              loading={loading}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              autoGroupColumnDef={autoGroupColumnDef}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Grouping by Null and Undefined Data](https://www.ag-grid.com/archive/36.2.0/examples/grouping-data/grouping-null-undefined/reactFunctionalTs/)

To enable unbalanced grouping, the following configuration is used:

```jsx
const groupAllowUnbalanced = true;

<AgGridReact groupAllowUnbalanced={groupAllowUnbalanced} />
```

## Hiding Parents of Individual Rows

Groups with only a single child can be hidden from the grid by setting the `groupHideParentOfSingleChild` grid option to `true`. To remove only groups with a single leaf child, set this option to `"leafGroupsOnly"` instead.

Filtering does not impact which groups get removed. Only groups containing a single child prior to filtering being applied are removed.

#### Removing Single Children

```tsx
"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,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
} from "ag-grid-enterprise";
import { getData } from "./data";

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

const modules = [
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  RowGroupingModule,
  SetFilterModule,
];

const GridExample = () => {
  const gridRef = useRef<AgGridReact>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<any[]>(getData());
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete" },
    { field: "country", rowGroup: true },
    { field: "city", rowGroup: true },
    { field: "year" },
    { 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 {
      headerName: "Group",
      field: "athlete",
      minWidth: 220,
      cellRenderer: "agGroupCellRenderer",
    };
  }, []);

  const onOptionChange = useCallback(() => {
    const key = (
      document.querySelector("#input-display-type") as HTMLSelectElement
    ).value;
    if (key === "true" || key === "false") {
      gridRef.current!.api.setGridOption(
        "groupHideParentOfSingleChild",
        key === "true",
      );
    } else {
      gridRef.current!.api.setGridOption(
        "groupHideParentOfSingleChild",
        "leafGroupsOnly",
      );
    }
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div className="example-header">
            <label>
              <span>groupHideParentOfSingleChild:</span>
              <select id="input-display-type" onChange={onOptionChange}>
                <option value="false">false</option>
                <option value="true">true</option>
                <option value="leafGroupsOnly">"leafGroupsOnly"</option>
              </select>
            </label>
          </div>

          <div style={gridStyle}>
            <AgGridReact
              ref={gridRef}
              rowData={rowData}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              autoGroupColumnDef={autoGroupColumnDef}
              groupDefaultExpanded={-1}
              suppressAggFuncInHeader={true}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Removing Single Children](https://www.ag-grid.com/archive/36.2.0/examples/grouping-data/remove-single-children/reactFunctionalTs/)

The following is an example of the configuration used to hide all parents of a single row:

```jsx
const groupHideParentOfSingleChild = true;

<AgGridReact groupHideParentOfSingleChild={groupHideParentOfSingleChild} />
```

> **Note**
>
> The properties `groupHideParentOfSingleChild` and `groupHideOpenParents` are mutually exclusive.
