---
title: "Column Groups"
framework: vue
version: "36.1.0"
---

# Column Groups

Columns can be grouped in the grid's header using Column Groups. Column groups can be shown as open / closed to show / hide child Columns.

Column Groups are configured by providing a hierarchy of Column Definitions. If a Column Definition contains the `children` attribute then the grid treats it as a Column Group.

#### Basic Grouping

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Name & Country",
        children: [{ field: "athlete" }, { field: "country" }],
      },
      {
        headerName: "Sports Results",
        children: [
          { columnGroupShow: "closed", field: "total" },
          { columnGroupShow: "open", field: "gold" },
          { columnGroupShow: "open", field: "silver" },
          { columnGroupShow: "open", field: "bronze" },
        ],
      },
    ]);
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Basic Grouping](https://www.ag-grid.com/examples/column-groups/basic-grouping/vue3)

```ts
<ag-grid-vue
    :columnDefs="columnDefs"
    /* other grid options ... */>
</ag-grid-vue>

this.columnDefs = [
    {
        headerName: 'Name & Country',
        children: [
            { field: 'athlete' },
            { field: 'country' }
        ]
    },
    {
        headerName: 'Sports Results',
        children: [
            { columnGroupShow: 'closed', field: 'total' },
            { columnGroupShow: 'open', field: 'gold' },
            { columnGroupShow: 'open', field: 'silver' },
            { columnGroupShow: 'open', field: 'bronze' },
        ],
    }
];
```

Set the attribute `columnGroupShow` on the group's children to set the expand and collapse policy as follows:

- **`'open'`:** The child is only shown when the group is open.
- **`'closed'`:** The child is only shown when the group is closed.
- **`null`, `undefined`:** The child is always shown.

See [Group Column Properties](https://www.ag-grid.com/vue-data-grid/column-properties/#reference-columnGroups) for all available properties.

## Group Defaults

Use `defaultColGroupDef` to set properties across all Column Groups.

#### Default Props

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColGroupDef="defaultColGroupDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        children: [{ field: "athlete" }, { field: "country" }],
      },
      {
        children: [
          { columnGroupShow: "closed", field: "total" },
          { columnGroupShow: "open", field: "gold" },
          { columnGroupShow: "open", field: "silver" },
          { columnGroupShow: "open", field: "bronze" },
        ],
      },
    ]);
    const defaultColGroupDef = ref<Partial<ColGroupDef>>({
      headerName: "A shared prop for all Groups",
    });
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColGroupDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Default Props](https://www.ag-grid.com/examples/column-groups/defaults/vue3)

```ts
<ag-grid-vue
    :defaultColGroupDef="defaultColGroupDef"
    /* other grid options ... */>
</ag-grid-vue>

this.defaultColGroupDef = {
    headerName: 'A shared prop for all Groups'
};
```

## Multiple Levels

The example below demonstrates a grid with many column group header levels. Note the following:

- The API is used to open and close groups. To do this, you will need to provide your groups with an ID during the definition, or look up the groups ID via the API (as an ID is generated if you don't provide one).
- The `colGroupDef.openByDefault` property is set on the E and F groups, resulting in these groups appearing as open by default.
- `defaultColGroupDef` and `defaultColDef` are used to apply a class to some of the headers. Using this technique, you can apply style to any of the header sections.

#### Advanced Grouping

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  HeaderClassParams,
  Icons,
  ModuleRegistry,
  NumberFilterModule,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  TextFilterModule,
  NumberFilterModule,
]);

function headerClassFunc(params: HeaderClassParams) {
  let foundC = false;
  let foundG = false;
  // for the bottom row of headers, column is present,
  // otherwise columnGroup is present. we are guaranteed
  // at least one is always present.
  let item = params.column ? params.column : params.columnGroup;
  // walk up the tree, see if we are in C or F groups
  while (item) {
    // if groupId is set then this must be a group.
    const colDef = item.getDefinition() as ColGroupDef;
    if (colDef.groupId === "GroupC") {
      foundC = true;
    } else if (colDef.groupId === "GroupG") {
      foundG = true;
    }
    item = item.getParent();
  }
  if (foundG) {
    return "column-group-g";
  } else if (foundC) {
    return "column-group-c";
  }
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div class="example-wrapper">
      <div class="button-bar">
        <button v-on:click="expandAll(true)">Expand All</button>
        <button v-on:click="expandAll(false)">Contract All</button>
      </div>
      <ag-grid-vue
        style="width: 100%; height: 100%;"
        @grid-ready="onGridReady"
        :columnDefs="columnDefs"
        :defaultColGroupDef="defaultColGroupDef"
        :defaultColDef="defaultColDef"
        :icons="icons"
        :rowData="rowData"></ag-grid-vue>
      </div>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Group A",
        groupId: "GroupA",
        children: [
          {
            headerName: "Athlete 1",
            field: "athlete",
            width: 150,
            filter: "agTextColumnFilter",
          },
          {
            headerName: "Group B",
            groupId: "GroupB",
            children: [
              { headerName: "Country 1", field: "country", width: 120 },
              {
                headerName: "Group C",
                groupId: "GroupC",
                children: [
                  { headerName: "Sport 1", field: "sport", width: 110 },
                  {
                    headerName: "Group D",
                    groupId: "GroupD",
                    children: [
                      {
                        headerName: "Total 1",
                        field: "total",
                        width: 100,
                        filter: "agNumberColumnFilter",
                      },
                      {
                        headerName: "Group E",
                        groupId: "GroupE",
                        openByDefault: true,
                        children: [
                          {
                            headerName: "Gold 1",
                            field: "gold",
                            width: 100,
                            filter: "agNumberColumnFilter",
                          },
                          {
                            headerName: "Group F",
                            groupId: "GroupF",
                            openByDefault: true,
                            children: [
                              {
                                headerName: "Silver 1",
                                field: "silver",
                                width: 100,
                                filter: "agNumberColumnFilter",
                              },
                              {
                                headerName: "Group G",
                                groupId: "GroupG",
                                children: [
                                  {
                                    headerName: "Bronze",
                                    field: "bronze",
                                    width: 100,
                                    filter: "agNumberColumnFilter",
                                  },
                                ],
                              },
                              {
                                headerName: "Silver 2",
                                columnGroupShow: "open",
                                field: "silver",
                                width: 100,
                                filter: "agNumberColumnFilter",
                              },
                            ],
                          },
                          {
                            headerName: "Gold 2",
                            columnGroupShow: "open",
                            field: "gold",
                            width: 100,
                            filter: "agNumberColumnFilter",
                          },
                        ],
                      },
                      {
                        headerName: "Total 2",
                        columnGroupShow: "open",
                        field: "total",
                        width: 100,
                        filter: "agNumberColumnFilter",
                      },
                    ],
                  },
                  {
                    headerName: "Sport 2",
                    columnGroupShow: "open",
                    field: "sport",
                    width: 110,
                  },
                ],
              },
              {
                headerName: "Country 2",
                columnGroupShow: "open",
                field: "country",
                width: 120,
              },
            ],
          },
          {
            headerName: "Age 2",
            columnGroupShow: "open",
            field: "age",
            width: 90,
            filter: "agNumberColumnFilter",
          },
        ],
      },
      {
        headerName: "Athlete 2",
        columnGroupShow: "open",
        field: "athlete",
        width: 150,
        filter: "agTextColumnFilter",
      },
    ]);
    const defaultColGroupDef = ref<Partial<ColGroupDef>>({
      headerClass: headerClassFunc,
    });
    const defaultColDef = ref<ColDef>({
      headerClass: headerClassFunc,
      filter: true,
    });
    const icons = ref<Icons>({
      columnGroupOpened: '<i class="far fa-minus-square"/>',
      columnGroupClosed: '<i class="far fa-plus-square"/>',
    });
    const rowData = ref<IOlympicData[]>(null);

    function expandAll(expand: boolean) {
      const groupNames = [
        "GroupA",
        "GroupB",
        "GroupC",
        "GroupD",
        "GroupE",
        "GroupF",
        "GroupG",
      ];
      groupNames.forEach((groupId) => {
        gridApi.value!.setColumnGroupOpened(groupId, expand);
      });
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColGroupDef,
      defaultColDef,
      icons,
      rowData,
      onGridReady,
      expandAll,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Advanced Grouping](https://www.ag-grid.com/examples/column-groups/advanced-grouping/vue3)

## Groups & Column Pinning

Pinned columns break groups. So if you have a group with 10 columns, 4 of which are inside the pinned area, two groups will be created, one with 4 (pinned) and one with 6 (not pinned).

## Groups & Column Moving

If you move columns so that columns in a group are no longer adjacent, then the group will again be broken and displayed as one or more groups in the grid.

Sometimes you want columns of the group to always stick together. To achieve this, set the column group property `marryChildren=true`. The example below demonstrates the following:

- Both 'Athlete Details' and 'Sports Results' have `marryChildren=true`.
- If you move columns inside these groups, you will not be able to move the column out of the group. For example, if you drag 'Athlete', it is not possible to drag it out of the 'Athlete Details' group.
- If you move a non group column, e.g. Age, it will not be possible to place it in the middle of a group and hence impossible to break the group apart.
- It is possible to place a column between groups (e.g. you can place 'Age' between the 'Athlete Details' and 'Sports Results').

#### Marry Children

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Athlete Details",
        marryChildren: true,
        children: [
          { field: "athlete", colId: "athlete" },
          { field: "country", colId: "country" },
        ],
      },
      { field: "age", colId: "age" },
      {
        headerName: "Sports Results",
        marryChildren: true,
        children: [
          { field: "sport", colId: "sport" },
          { field: "total", colId: "total" },
          { field: "gold", colId: "gold" },
          { field: "silver", colId: "silver" },
          { field: "bronze", colId: "bronze" },
        ],
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 160,
    });
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Marry Children](https://www.ag-grid.com/examples/column-groups/marry-children/vue3)

## Resizing Groups

If you grab the group resize bar, it resizes each child in the group evenly distributing the new additional width. If you grab the child resize bar, only that one column will be resized.

![Header Resize](https://www.ag-grid.com/_astro/header-resize.B566By6b.png)

## Auto Header Height

The header row for the column groups can have its height set automatically based on the content of the group header cells. This is most useful when using the `wrapHeaderText` column group property.

To enable this, set `autoHeaderHeight=true` on the column group definition you want to adjust the height for. If more than one column group has this property enabled, then the header row will be sized to the maximum of these column groups' header cells to avoid content overflow.

The example below demonstrates using the `autoHeaderHeight` property in conjunction with the `wrapHeaderText` property, so that long column group names are fully displayed.

- Note that the long column group header names wrap onto another line
- Resize a column group down by dragging the resize handle on the column group header or child column headers left. Observe that the group header row will expand so the header cell content is still fully visible as it's getting wrapped on multiple lines.

#### Auto Header Height

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        children: [{ field: "athlete" }, { field: "country" }],
      },
      {
        headerName: "A shared prop for all Groups",
        wrapHeaderText: true,
        autoHeaderHeight: true,
        children: [
          { columnGroupShow: "closed", field: "total" },
          { columnGroupShow: "open", field: "gold" },
          { columnGroupShow: "open", field: "silver" },
          { columnGroupShow: "open", field: "bronze" },
        ],
      },
    ]);
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Auto Header Height](https://www.ag-grid.com/examples/column-groups/auto-height/vue3)

## Colouring Groups

The grid does not automatically colour the groups for you. However, you can achieve this by using the `headerClass` or `headerStyle` properties in the column definitions. These attributes can be applied to both individual columns and column groups.

```ts
<ag-grid-vue
    :columnDefs="columnDefs"
    /* other grid options ... */>
</ag-grid-vue>

this.columnDefs = [
    // the CSS class name supplied to 'headerClass' will get applied to the header group
    { headerName: 'Athlete Details', headerClass: 'my-css-class', children: [] },
    { headerName: 'Medal Details', headerStyle: { backgroundColor: 'green' }, children: [] }
];
```

## Text Alignment

The labels in the grouping headers are positioned with `display: flex`. To make the group headers right-aligned, add the following rule set in your application, after the grid's style sheets:

```css
.ag-header-group-cell-label {
    flex-direction: row-reverse;
}
```

## Sticky Label

When Column Groups are too wide, the **Header Label** is always visible while scrolling the grid horizontally. To suppress this behaviour, set the column group property `suppressStickyLabel=true`. The example below demonstrates the following:

- Both 'Athlete Details' and 'Sports Results' have `suppressStickyLabel=true`.
- If you scroll the grid horizontally, the header label will not remain visible as the column is partially scrolled out of view.

#### Sticky Label

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Athlete Details",
        suppressStickyLabel: true,
        children: [
          { field: "athlete", pinned: true, colId: "athlete" },
          { field: "country", colId: "country" },
          { field: "age", colId: "age" },
        ],
      },
      {
        headerName: "Sports Results",
        suppressStickyLabel: true,
        openByDefault: true,
        children: [
          { field: "sport", colId: "sport" },
          { field: "gold", colId: "gold", columnGroupShow: "open" },
          { field: "silver", colId: "silver", columnGroupShow: "open" },
          { field: "bronze", colId: "bronze", columnGroupShow: "open" },
          { field: "total", colId: "total", columnGroupShow: "closed" },
        ],
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 200,
    });
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Sticky Label](https://www.ag-grid.com/examples/column-groups/suppress-sticky-label/vue3)

## Group Changes

Similar to adding and removing columns, you can also add and remove column groups. If the column definitions passed in have column groups, then the columns will be grouped to the new configuration.

The example below shows adding and removing groups to columns. Note the following:

- Select **No Groups** to show all columns without any grouping.
- Select **Participant in Group** to show all participant columns only in a group.
- Select **Medals in Group** to show all medal columns only in a group.
- Select **Participant and Medals in Group** to show participant and medal columns in groups.
- As groups are added and removed, note that the state of the individual columns is preserved. To observe this, try moving, resizing, sorting, filtering etc and then add and remove groups, all the changed state will be preserved.

#### Group Changes

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberFilterModule,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextFilterModule,
  NumberFilterModule,
  ClientSideRowModelModule,
]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div class="test-container">
      <div class="test-header">
        <label>
          <button v-on:click="onBtNoGroups()">No Groups</button>
        </label>
        <label>
          <div class="participant-group legend-box"></div>
          <button v-on:click="onParticipantInGroupOnly()">Participant in Group</button>
        </label>
        <label>
          <div class="medals-group legend-box"></div>
          <button v-on:click="onMedalsInGroupOnly()">Medals in Group</button>
        </label>
        <label>
          <div class="participant-group legend-box"></div>
          <div class="medals-group legend-box"></div>
          <button v-on:click="onParticipantAndMedalsInGroups()">Participant and Medals in Group</button>
        </label>
      </div>
      <ag-grid-vue
        style="width: 100%; height: 100%;"
        class="test-grid"
        @grid-ready="onGridReady"
        :columnDefs="columnDefs"
        :defaultColDef="defaultColDef"
        :maintainColumnOrder="true"
        :rowData="rowData"></ag-grid-vue>
      </div>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<ColDef[]>([
      { field: "athlete", colId: "athlete" },
      { field: "age", colId: "age" },
      { field: "country", colId: "country" },
      { field: "year", colId: "year" },
      { field: "date", colId: "date" },
      { field: "total", colId: "total" },
      { field: "gold", colId: "gold" },
      { field: "silver", colId: "silver" },
      { field: "bronze", colId: "bronze" },
    ]);
    const defaultColDef = ref<ColDef>({
      initialWidth: 150,
      filter: true,
    });
    const rowData = ref<IOlympicData[]>(null);

    function onBtNoGroups() {
      const columnDefs: ColDef[] = [
        { field: "athlete", colId: "athlete" },
        { field: "age", colId: "age" },
        { field: "country", colId: "country" },
        { field: "year", colId: "year" },
        { field: "date", colId: "date" },
        { field: "total", colId: "total" },
        { field: "gold", colId: "gold" },
        { field: "silver", colId: "silver" },
        { field: "bronze", colId: "bronze" },
      ];
      gridApi.value!.setGridOption("columnDefs", columnDefs);
    }
    function onMedalsInGroupOnly() {
      const columnDefs: (ColDef | ColGroupDef)[] = [
        { field: "athlete", colId: "athlete" },
        { field: "age", colId: "age" },
        { field: "country", colId: "country" },
        { field: "year", colId: "year" },
        { field: "date", colId: "date" },
        {
          headerName: "Medals",
          headerClass: "medals-group",
          children: [
            { field: "total", colId: "total" },
            { field: "gold", colId: "gold" },
            { field: "silver", colId: "silver" },
            { field: "bronze", colId: "bronze" },
          ],
        },
      ];
      gridApi.value!.setGridOption("columnDefs", columnDefs);
    }
    function onParticipantInGroupOnly() {
      const columnDefs: (ColDef | ColGroupDef)[] = [
        {
          headerName: "Participant",
          headerClass: "participant-group",
          children: [
            { field: "athlete", colId: "athlete" },
            { field: "age", colId: "age" },
            { field: "country", colId: "country" },
            { field: "year", colId: "year" },
            { field: "date", colId: "date" },
          ],
        },
        { field: "total", colId: "total" },
        { field: "gold", colId: "gold" },
        { field: "silver", colId: "silver" },
        { field: "bronze", colId: "bronze" },
      ];
      gridApi.value!.setGridOption("columnDefs", columnDefs);
    }
    function onParticipantAndMedalsInGroups() {
      const columnDefs: (ColDef | ColGroupDef)[] = [
        {
          headerName: "Participant",
          headerClass: "participant-group",
          children: [
            { field: "athlete", colId: "athlete" },
            { field: "age", colId: "age" },
            { field: "country", colId: "country" },
            { field: "year", colId: "year" },
            { field: "date", colId: "date" },
          ],
        },
        {
          headerName: "Medals",
          headerClass: "medals-group",
          children: [
            { field: "total", colId: "total" },
            { field: "gold", colId: "gold" },
            { field: "silver", colId: "silver" },
            { field: "bronze", colId: "bronze" },
          ],
        },
      ];
      gridApi.value!.setGridOption("columnDefs", columnDefs);
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
      onBtNoGroups,
      onMedalsInGroupOnly,
      onParticipantInGroupOnly,
      onParticipantAndMedalsInGroups,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Group Changes](https://www.ag-grid.com/examples/column-groups/group-changes/vue3)

The example above shows adding and removing groups. It is also possible to add and remove columns from groups. This is demonstrated in the example below. Note the following:

- The example has two groups: **Athlete Details** and **Sports Results**
- The example has two sets of columns, **Normal Cols** and **Extra Cols**.
- When you move from **Normal Cols** to **Extra Cols**, three new columns are added to the list. Two belong to the **Athlete Details** group, the other belongs to no group.

#### Group Changes 2

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

function createNormalColDefs(): (ColDef | ColGroupDef)[] {
  return [
    {
      headerName: "Athlete Details",
      headerClass: "participant-group",
      children: [
        { field: "athlete", colId: "athlete" },
        { field: "country", colId: "country" },
      ],
    },
    { field: "age", colId: "age" },
    {
      headerName: "Sports Results",
      headerClass: "medals-group",
      children: [
        { field: "sport", colId: "sport" },
        { field: "gold", colId: "gold" },
      ],
    },
  ];
}

function createExtraColDefs(): (ColDef | ColGroupDef)[] {
  return [
    {
      headerName: "Athlete Details",
      headerClass: "participant-group",
      children: [
        { field: "athlete", colId: "athlete" },
        { field: "country", colId: "country" },
        { field: "region1", colId: "region1" },
        { field: "region2", colId: "region2" },
      ],
    },
    { field: "age", colId: "age" },
    { field: "distance", colId: "distance" },
    {
      headerName: "Sports Results",
      headerClass: "medals-group",
      children: [
        { field: "sport", colId: "sport" },
        { field: "gold", colId: "gold" },
      ],
    },
  ];
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div class="test-container">
      <div class="test-header">
        <button v-on:click="onBtNormalCols()">Normal Cols</button>
        <button v-on:click="onBtExtraCols()">Extra Cols</button>
      </div>
      <ag-grid-vue
        style="width: 100%; height: 100%;"
        class="test-grid"
        @grid-ready="onGridReady"
        :defaultColDef="defaultColDef"
        :columnDefs="columnDefs"
        :rowData="rowData"></ag-grid-vue>
      </div>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const defaultColDef = ref<ColDef>({
      width: 150,
    });
    const columnDefs = ref<ColDef[]>(createNormalColDefs());
    const rowData = ref<IOlympicData[]>(null);

    function onBtNormalCols() {
      gridApi.value!.setGridOption("columnDefs", createNormalColDefs());
    }
    function onBtExtraCols() {
      gridApi.value!.setGridOption("columnDefs", createExtraColDefs());
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      defaultColDef,
      columnDefs,
      rowData,
      onGridReady,
      onBtNormalCols,
      onBtExtraCols,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Group Changes 2](https://www.ag-grid.com/examples/column-groups/group-changes-2/vue3)

## Column Height

By default the grid will resize the header cell to span the whole height of the header container, as shown in the example below.

Note the following:

- The **Age** column header cell is not under a column group cell, but spans the entire height of the header container.

#### Span Header Height

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Athlete Details",
        children: [{ field: "athlete" }, { field: "country" }],
      },
      {
        field: "age",
        width: 90,
      },
    ]);
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Span Header Height](https://www.ag-grid.com/examples/column-groups/span-header-height/vue3)

Using the **Column Property** `suppressSpanHeaderHeight` the Grid will balance the column headers with different number of levels with an empty column group header cell, as shown in the example below.

```ts
<ag-grid-vue
    :columnDefs="columnDefs"
    /* other grid options ... */>
</ag-grid-vue>

this.columnDefs = [
    {
        headerName: 'Athlete Details',
        children: [
            { field: 'athlete' },
            { field: 'country' },
        ],
    },
    {
        field: 'age',
        width: 90,
        suppressSpanHeaderHeight: true,
    }
];
```

Note the following:

- The **Age** column has an empty column group header cell above it (shown with red borders).

#### Padded Header

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Athlete Details",
        children: [{ field: "athlete" }, { field: "country" }],
      },
      {
        field: "age",
        width: 90,
        suppressSpanHeaderHeight: true,
      },
    ]);
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Padded Header](https://www.ag-grid.com/examples/column-groups/padded-header/vue3)

## Hide Padded Header Rows

When using column groups the grid adds padding to columns to ensure the column tree is balanced. When a column with a deeper tree is hidden, this can lead to header rows consisting entirely of padding. Set the `hidePaddedHeaderRows` grid option to `true` to hide rows consisting of only padding.

#### Hide Padded Header Rows

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div class="example-wrapper">
      <div class="example-header">
        <label>
          <span>hidePaddedHeaderRows:</span>
          <input id="hidePaddedHeaderRows" type="checkbox" v-on:change="toggleOption()">
          </label>
        </div>
        <ag-grid-vue
          style="width: 100%; height: 100%;"
          @grid-ready="onGridReady"
          :columnDefs="columnDefs"
          :defaultColDef="defaultColDef"
          :rowData="rowData"></ag-grid-vue>
        </div>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Athlete Details",
        children: [
          { field: "athlete" },
          {
            headerName: "Meta Data",
            columnGroupShow: "open",
            children: [{ field: "country" }, { field: "sport" }],
          },
        ],
      },
      { field: "gold" },
      { field: "silver" },
      { field: "bronze" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
    });
    const rowData = ref<any[]>(null);

    function toggleOption() {
      const isChecked = document.querySelector<HTMLInputElement>(
        "#hidePaddedHeaderRows",
      )!.checked;
      gridApi.value.setGridOption("hidePaddedHeaderRows", isChecked);
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
      toggleOption,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Hide Padded Header Rows](https://www.ag-grid.com/examples/column-groups/hide-padded-header-rows/vue3)

The example above demonstrates a grid with hidden column groups, causing the grid headers to be taller than necessary when the parent group is collapsed.

## Tooltips

Tooltips can be added to the Column Group Headers by using the `headerTooltip` property of the `ColGroupDef`.

The example below demonstrates using the `headerTooltip` property in the grid column groups.

#### Header Tooltip

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  TooltipModule,
  enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([TooltipModule, ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Name & Country",
        headerTooltip: "Name & Country Group",
        children: [{ field: "athlete" }, { field: "country" }],
      },
      {
        headerName: "Sports Results",
        headerTooltip: "Sports Results Group",
        children: [
          { columnGroupShow: "closed", field: "total" },
          { columnGroupShow: "open", field: "gold" },
          { columnGroupShow: "open", field: "silver" },
          { columnGroupShow: "open", field: "bronze" },
        ],
      },
    ]);
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Header Tooltip](https://www.ag-grid.com/examples/column-groups/group-header-tooltip/vue3)

## Inner Header Group Component

When using the Header Group Component, the `agColumnHeaderGroup` component will display the header group name, adjacent to the expand / collapse button.

This text value can be overridden with a [Custom Component](https://www.ag-grid.com/vue-data-grid/components/) by setting the `innerHeaderGroupComponent` and `innerHeaderGroupComponentParams` properties on the `headerGroupComponentParams` property. This is useful when you only need to implement a Component to customise the **Column Group Name** without having to reimplement the other header group functionality such as the expand / collapse.

```js
colDef = {
    ...
    headerGroupComponentParams : {
        innerHeaderGroupComponent: 'MyInnerHeaderGroupComponent',
        innerHeaderGroupComponentParams: {
            currencySymbol: '£' // the pound symbol will be placed into params
        }
    }
}
```

#### Custom Inner Header Group Component

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./style.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import CustomInnerHeaderGroup from "./customInnerHeaderGroupVue";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
    CustomInnerHeaderGroup,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Athlete Details",
        headerGroupComponentParams: {
          innerHeaderGroupComponent: "CustomInnerHeaderGroup",
          icon: "fa-user",
        },
        children: [
          { field: "athlete", width: 150 },
          { field: "age", width: 90, columnGroupShow: "open" },
          {
            field: "country",
            width: 120,
            columnGroupShow: "open",
          },
        ],
      },
      {
        headerName: "Medal details",
        headerGroupComponentParams: {
          innerHeaderGroupComponent: "CustomInnerHeaderGroup",
        },
        children: [
          { field: "year", width: 90 },
          { field: "date", width: 110 },
          {
            field: "sport",
            width: 110,
            columnGroupShow: "open",
          },
          {
            field: "gold",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "silver",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "bronze",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "total",
            width: 100,
            columnGroupShow: "open",
          },
        ],
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 100,
    });
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Custom Inner Header Group Component](https://www.ag-grid.com/examples/column-groups/inner-header-group-component/vue3)

Any valid Vue component can be a custom inner header group component, however it must implement the `IHeaderGroup` interface:

### IHeaderGroup

```ts

interface IHeaderGroup {
  // Optional: update the component in place instead of recreating it (e.g. when the group header name
  // is edited). Return `true` if the update was handled, `false` to have the grid recreate the component.
  refresh?(params: IHeaderGroupParams): boolean;

}
```

## Selecting Components

By default the grid uses the provided Header Group Component. To use a Custom Group Component set `headerGroupComponent` on the Column Definition.

```js
const colDefs = [
    {
        headerName: "Athlete Details",
        headerGroupComponent: "MyCustomGroupComp", // Custom Comp
        children: [
            {field: "name"},
            {field: "country"}
        ]
    }
]
```

See [Registering Components](https://www.ag-grid.com/vue-data-grid/components/) for an overview of registering components.

## Custom Group Component

The example below shows a Custom Column Group Component.

#### Header Group

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./style.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import CustomHeaderGroup from "./customHeaderGroupVue";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
    CustomHeaderGroup,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Athlete Details",
        headerGroupComponent: "CustomHeaderGroup",
        children: [
          { field: "athlete", width: 150 },
          { field: "age", width: 90, columnGroupShow: "open" },
          {
            field: "country",
            width: 120,
            columnGroupShow: "open",
          },
        ],
      },
      {
        headerName: "Medal details",
        headerGroupComponent: "CustomHeaderGroup",
        children: [
          { field: "year", width: 90 },
          { field: "date", width: 110 },
          {
            field: "sport",
            width: 110,
            columnGroupShow: "open",
          },
          {
            field: "gold",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "silver",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "bronze",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "total",
            width: 100,
            columnGroupShow: "open",
          },
        ],
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 100,
    });
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Header Group](https://www.ag-grid.com/examples/column-groups/header-group-component/vue3)

As with Column Headers, the grid will always handle resize and column moving. The Custom Component is responsible for the following:

- **Group Open / Close:** If the group can expand (one or more columns visibility depends on the open / closed state of the group) then the Custom Component should handle the interaction with the user for opening and closing groups.

The `params` available to the Header Group Component are as follows:

Properties available on the `IHeaderGroupParams&lt;TData = any, TContext = any&gt;` interface.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `columnGroup` | [`ColumnGroup`](https://www.ag-grid.com/vue-data-grid/column-object-group/) |  |  | The column group the header is for. |
| `displayName` | `string` |  |  | The text label to render. If the column is using a headerValueGetter, the displayName will take this into account. |
| `setExpanded` | `Function` |  |  | Opens / closes the column group |
| `setTooltip` | `Function` |  |  | Sets a tooltip to the main element of this component. `value` The value to be displayed by the tooltip `shouldDisplayTooltip` A function returning a boolean that allows the tooltip to be displayed conditionally. This option does not work when `enableBrowserTooltips={true}`. |
| `showColumnMenu` | `Function` |  |  | Callback to request the grid to show the column menu. Pass in an html element to have the grid position the menu over the element. If provided, the grid will call `onClosedCallback` when the menu is closed. Note that this only works with the new column menu. |
| `showColumnMenuAfterMouseClick` | `Function` |  |  | Callback to request the grid to show the column menu. Similar to `showColumnMenu`, but will position the menu next to the provided `mouseEvent`. If provided, the grid will call `onClosedCallback` when the menu is closed. Note that this only works with the new column menu. |
| `innerHeaderGroupComponent` | `any` |  |  | The component to use for inside the header group (replaces the text value and leaves the remainder of the Grid's original component). |
| `innerHeaderGroupComponentParams` | `any` |  |  | Additional params to customise to the `innerHeaderGroupComponent`. |
| `eGridHeader` | [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) |  |  | The header the grid provides. The custom group header component is a child of the grid provided header. The grid's header component is what contains the grid managed functionality such as resizing, keyboard navigation etc. This is provided should you want to make changes to this cell, eg add ARIA tags, or add keyboard event listener (as focus goes here when navigating to the header). |
| `api` | [`GridApi`](https://www.ag-grid.com/vue-data-grid/grid-api/) |  |  | The grid api. |
| `context` | [`TContext`](https://www.ag-grid.com/vue-data-grid/typescript-generics/#context-tcontext) |  |  | Application context as set on `gridOptions.context`. |

Not all column groups can open and close, so you should display open / close features accordingly. To check if a column group should have open / close functionality, check the `isExpandable()` method on the column group.

```js
const showExpandableIcons = this.params.columnGroup.isExpandable()
```

To check if a column group is open or closed, check the `isExpanded()` method on the column group.

```js
const groupIsOpen = this.params.columnGroup.isExpanded();
```

To open / close a column group, use the `this.params.setExpanded(boolean)` method.

```js
// this code toggles the expanded state
const oldValue = this.params.columnGroup.isExpanded();
const newValue = !oldValue;
this.params.setExpanded(newValue);
```

To know if a group is expanded or collapsed, listen for the `expandedChanged` event on the column group.

```js
// get a reference to the original column group
const columnGroup = this.params.columnGroup.getProvidedColumnGroup();
// create listener
const listener = () => { console.log('group was opened or closed'); };
// add listener
columnGroup.addEventListener('expandedChanged', listener);

// don't forget to remove the listener in your destroy method
columnGroup.removeEventListener('expandedChanged', listener);
```

### Dynamic Tooltips

When using Custom Header Components it might be necessary to have a better control of how `Tooltips` are added instead of simply using the `headerTooltip` config. For this purpose, we provide the `setTooltip` method.

Properties available on the `IHeaderGroupParams&lt;TData = any, TContext = any&gt;` interface.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `setTooltip` | `Function` |  |  | Sets a tooltip to the main element of this component. `value` The value to be displayed by the tooltip `shouldDisplayTooltip` A function returning a boolean that allows the tooltip to be displayed conditionally. This option does not work when `enableBrowserTooltips={true}`. |

The example below demonstrates using the Dynamic Tooltips with a Custom Group Component.

- Note that only Group Headers where the text is not fully displayed will show tooltips.

#### Dynamic Group Header Tooltip

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./style.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  TooltipModule,
  enableDevValidations,
} from "ag-grid-community";
import CustomHeaderGroup from "./customHeaderGroupVue";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ClientSideRowModelModule, TooltipModule]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
    CustomHeaderGroup,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<(ColDef | ColGroupDef)[]>([
      {
        headerName: "Athlete Details",
        headerGroupComponent: "CustomHeaderGroup",
        children: [
          { field: "athlete", width: 120 },
          { field: "age", width: 90, columnGroupShow: "open" },
          {
            field: "country",
            width: 120,
            columnGroupShow: "open",
          },
        ],
      },
      {
        headerName: "Medal details",
        headerGroupComponent: "CustomHeaderGroup",
        children: [
          { field: "year", width: 90 },
          { field: "date", width: 110 },
          {
            field: "sport",
            width: 110,
            columnGroupShow: "open",
          },
          {
            field: "gold",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "silver",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "bronze",
            width: 100,
            columnGroupShow: "open",
          },
          {
            field: "total",
            width: 100,
            columnGroupShow: "open",
          },
        ],
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 100,
    });
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => (rowData.value = data);

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Dynamic Group Header Tooltip](https://www.ag-grid.com/examples/column-groups/dynamic-tooltips/vue3)
