---
title: "Aligned Grids"
framework: vue
version: "36.1.0"
---

# Aligned Grids

Aligning two or more grids means columns will be kept aligned in all grids. In other words, column changes to one grid (column width, column order, column visibility, etc.) are reflected in the other grid. This is useful if you have two grids, one above the other such that their columns are vertically aligned, and you want to keep the columns aligned.

## Configuration

Configure via the grid option `alignedGrids`.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `alignedGrids` | `AlignedGrid[] \| (() => AlignedGrid[])` |  |  | A list of grids to treat as Aligned Grids. Provide a list if the grids / apis already exist or return via a callback to allow the aligned grids to be retrieved asynchronously. If grids are aligned then the columns and horizontal scrolling will be kept in sync. Module: [`AlignedGridsModule`](https://www.ag-grid.com/vue-data-grid/modules/). |

Align grids in your component by providing the `alignedGrids` property with the other grids `GridApi`.

```js
gridOptionsSecond = {
    alignedGrids: () => [this.firstApi],
}
```

Align grids in your component by providing `alignedGrids` with a reference to the other `AgGridVue` component.

```js
// Annotate with a template reference
<ag-grid-vue ref="firstGrid"

gridOptionsSecond = {
    // register first grid to receive events from the second
    alignedGrids: () => [this.$refs.firstGrid],
}
```

Alternatively it is possible to align grids via refs in your component template.

```js
// Link using template variables
<ag-grid-vue
  ref="firstGrid" :alignedGrids="[this.$refs.secondGrid]" ...>
<ag-grid-vue
  ref="secondGrid" :alignedGrids="[this.$refs.firstGrid]" ...>

```

## Example: Aligned Grids

Below shows two grids, both aligned with the other (so any column change to one will be reflected in the other). The following should be noted:

- When either grid is scrolled horizontally, the other grid follows.
- Showing / hiding a column on either grid (via the checkbox) will show / hide the column on the other grid, despite the API being called on one grid only.
- When a column is resized on either grid, the other grid follows.
- When a column group is opened on either grid, the other grid follows.

#### Aligned Grids

```ts
import { createApp, defineComponent } from "vue";

import type {
  ColDef,
  ColGroupDef,
  GridOptions,
  GridReadyEvent,
} from "ag-grid-community";
import {
  AlignedGridsModule,
  ClientSideRowModelModule,
  ColumnApiModule,
  ColumnAutoSizeModule,
  ModuleRegistry,
  NumberFilterModule,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import { AgGridVue } from "ag-grid-vue3";

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

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

const VueExample = defineComponent({
  template: `
        <div style="height: 100%; display: flex; flex-direction: column">
            <div style="flex: 0 1 auto;">
                <label><input type="checkbox" checked @change="onCbAthlete($event.target.checked)"/>Athlete</label>
                <label><input type="checkbox" checked @change="onCbAge($event.target.checked)"/>Age</label>
                <label><input type="checkbox" checked @change="onCbCountry($event.target.checked)"/>Country</label>
            </div>
            <ag-grid-vue style="flex: 1 1 auto;"
                         ref="topGrid"
                         :columnDefs="columnDefs"
                         :rowData="rowData"
                         
                         :gridOptions="topOptions"
                         @grid-ready="onGridReady($event)">
            </ag-grid-vue>
            <div style='height: 5%'></div>
            <ag-grid-vue style="flex: 1 1 auto;"
                         ref="bottomGrid"
                         :columnDefs="columnDefs"
                         :rowData="rowData"
                         
                         :gridOptions="bottomOptions">
            </ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  data: function () {
    return {
      columnDefs: <(ColDef | ColGroupDef)[]>[
        { field: "athlete" },
        { field: "age" },
        { field: "country" },
        { field: "year" },
        { field: "sport" },
        {
          headerName: "Medals",
          children: [
            {
              columnGroupShow: "closed",
              colId: "total",
              valueGetter: "data.gold + data.silver + data.bronze",
              width: 200,
            },
            { columnGroupShow: "open", field: "gold", width: 100 },
            { columnGroupShow: "open", field: "silver", width: 100 },
            { columnGroupShow: "open", field: "bronze", width: 100 },
          ],
        },
      ],
      rowData: [],
      topOptions: <GridOptions>{
        alignedGrids: () => [this.$refs.bottomGrid],
        defaultColDef: {
          filter: true,
          minWidth: 100,
        },
        autoSizeStrategy: {
          type: "fitGridWidth",
        },
      },
      bottomOptions: <GridOptions>{
        alignedGrids: () => [this.$refs.topGrid],
        defaultColDef: {
          filter: true,
          minWidth: 100,
        },
      },
    };
  },
  mounted() {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((rowData) => (this.rowData = rowData));
  },
  methods: {
    onCbAthlete(value) {
      // we only need to update one grid, as the other is a slave
      this.topGridApi.setColumnsVisible(["athlete"], value);
    },

    onCbAge(value) {
      // we only need to update one grid, as the other is a slave
      this.topGridApi.setColumnsVisible(["age"], value);
    },

    onCbCountry(value) {
      // we only need to update one grid, as the other is a slave
      this.topGridApi.setColumnsVisible(["country"], value);
    },

    onGridReady(params: GridReadyEvent) {
      this.topGridApi = params.api;
    },
  },
});

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

[Live example: Aligned Grids](https://www.ag-grid.com/examples/aligned-grids/aligned-grids/vue3)

## Events

The events which are fired as part of the grid alignment relationship are as follows:

- Horizontal Scroll
- Column Hidden / Shown
- Column Moved
- Column Group Opened / Closed
- Column Resized
- Column Pinned

## Pivots

The pivot functionality does not work with aligned grids. This is because pivoting data changes the columns, which would make the aligned grids incompatible, as they are no longer sharing the same set of columns.

## Example: Aligned Grid as Footer

So why would you want to align grids like this? It's great for aligning grids that have different data but similar columns. Maybe you want to include a footer grid with 'summary' data. Maybe you have two sets of data, but one is aggregated differently to the other.

This example is a bit more useful. In the bottom grid, we show a summary row. Also note the following:

- The top grid has no horizontal scroll bar, suppressed via a grid option.
- The bottom grid has no header, suppressed via a grid option.
- `autoSizeStrategy` is only passed to the top grid, the bottom grid receives the new column widths from the top grid.

#### Aligned Grid as Footer

```ts
import { createApp, defineComponent } from "vue";

import type { ColDef, ColGroupDef, GridOptions } from "ag-grid-community";
import {
  AlignedGridsModule,
  ClientSideRowModelModule,
  ColumnAutoSizeModule,
  ModuleRegistry,
  NumberFilterModule,
  RowStyleModule,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import { AgGridVue } from "ag-grid-vue3";

import "./styles.css";

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

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

const VueExample = defineComponent({
  template: `
        <div style="height: 100%; display: flex; flex-direction: column">
            <ag-grid-vue style="flex: 1 1 auto;"
                         ref="topGrid"
                         class="top"
                         :gridOptions="topGridOptions"
                         :columnDefs="columnDefs"
                         :rowData="rowData"
                         >
            </ag-grid-vue>
            <ag-grid-vue style="height: 60px; flex: none;"
                         ref="bottomGrid"
                         class="bottom"
                         :gridOptions="bottomGridOptions"
                         :headerHeight="0"
                         :columnDefs="columnDefs"
                         :rowData="bottomData"
                         :rowStyle="rowStyle">
            </ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  data: function () {
    return {
      topGridOptions: null,
      bottomGridOptions: null,
      gridApi: null,
      rowData: null,
      bottomData: null,
      columnDefs: null,
      athleteVisible: true,
      ageVisible: true,
      countryVisible: true,
      rowStyle: { fontWeight: "bold" },
    };
  },
  beforeMount() {
    this.bottomData = [
      {
        athlete: "Total",
        age: "15 - 61",
        country: "Ireland",
        year: "2020",
        date: "26/11/1970",
        sport: "Synchronised Riding",
        gold: 55,
        silver: 65,
        bronze: 12,
      },
    ];

    this.topGridOptions = <GridOptions>{
      alignedGrids: () => [this.$refs.bottomGrid],
      defaultColDef: {
        filter: true,
        minWidth: 100,
      },
      suppressHorizontalScroll: true,
      alwaysShowVerticalScroll: true,
      autoSizeStrategy: {
        type: "fitCellContents",
      },
    };
    this.bottomGridOptions = <GridOptions>{
      alignedGrids: () => [this.$refs.topGrid],
      defaultColDef: {
        filter: true,
        flex: 1,
        minWidth: 100,
      },
      alwaysShowVerticalScroll: true,
    };

    this.columnDefs = <(ColDef | ColGroupDef)[]>[
      { field: "athlete", width: 200, hide: !this.athleteVisible },
      { field: "age", width: 150, hide: !this.ageVisible },
      { field: "country", width: 150, hide: !this.countryVisible },
      { field: "year", width: 120 },
      { field: "date", width: 150 },
      { field: "sport", width: 150 },
      {
        headerName: "Total",
        colId: "total",
        valueGetter: "data.gold + data.silver + data.bronze",
        width: 200,
      },
      { field: "gold", width: 100 },
      { field: "silver", width: 100 },
      { field: "bronze", width: 100 },
    ];
  },
  mounted() {
    this.gridApi = this.$refs.topGrid.api;

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

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

[Live example: Aligned Grid as Footer](https://www.ag-grid.com/examples/aligned-grids/aligned-floating-footer/vue3)

## Example: Align Column Groups

It is possible that you have column groups that are split because of pinning or the order of the columns. The grid below has only two groups that are split, displayed as many split groups. The column aligning also works here in that a change to a split group will open / close all the instances of that group in both tables.

#### Aligned Column Groups

```ts
import { createApp, defineComponent } from "vue";

import type { ColDef, ColGroupDef, GridOptions } from "ag-grid-community";
import {
  AlignedGridsModule,
  ClientSideRowModelModule,
  ColumnApiModule,
  ColumnAutoSizeModule,
  ModuleRegistry,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import { AgGridVue } from "ag-grid-vue3";

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

ModuleRegistry.registerModules([
  TextFilterModule,
  ColumnAutoSizeModule,
  ColumnApiModule,
  AlignedGridsModule,
  ClientSideRowModelModule,
]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
            <ag-grid-vue
                style="width: 100%; height: 45%;"
                ref="topGrid"
                :gridOptions="topOptions"
                :columnDefs="columnDefs"
                :rowData="rowData">
            </ag-grid-vue>
            <div style='height: 5%'></div>
            <ag-grid-vue
                style="width: 100%; height: 45%;"
                ref="bottomGrid"
                :gridOptions="bottomOptions"
                :columnDefs="columnDefs"
                :rowData="rowData">
            </ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  data: function () {
    return {
      topOptions: <GridOptions>{
        alignedGrids: () => [this.$refs.bottomGrid],
        defaultColDef: {
          filter: true,
          minWidth: 120,
        },
        autoSizeStrategy: {
          type: "fitGridWidth",
        },
      },
      bottomOptions: <GridOptions>{
        alignedGrids: () => [this.$refs.topGrid],
        defaultColDef: {
          filter: true,
          minWidth: 120,
        },
      },
      topGridApi: null,
      columnDefs: <(ColDef | ColGroupDef)[]>[
        {
          headerName: "Group 1",
          groupId: "Group1",
          children: [
            { field: "athlete", pinned: true },
            { field: "age", pinned: true, columnGroupShow: "open" },
            { field: "country" },
            { field: "year", columnGroupShow: "open" },
            { field: "date" },
            { field: "sport", columnGroupShow: "open" },
          ],
        },
        {
          headerName: "Group 2",
          groupId: "Group2",
          children: [
            { field: "athlete", pinned: true },
            { field: "age", pinned: true, columnGroupShow: "open" },
            { field: "country" },
            { field: "year", columnGroupShow: "open" },
            { field: "date" },
            { field: "sport", columnGroupShow: "open" },
          ],
        },
      ],

      rowData: null,
    };
  },
  mounted() {
    this.topGridApi = this.$refs.topGrid.api;

    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((rowData) => {
        this.rowData = rowData;

        // mix up some columns
        this.topGridApi.moveColumnByIndex(11, 4);
        this.topGridApi.moveColumnByIndex(11, 4);
      });
  },
});

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

[Live example: Aligned Column Groups](https://www.ag-grid.com/examples/aligned-grids/aligned-column-groups/vue3)

## Event Propagation

When a grid fires an event, it will be processed by all registered aligned grids. However if a grid is processing such an event, it will not fire an event to other aligned grids. For example, consider the grids A, B and C where B is aligned to A and C is aligned to B (ie A -> B -> C). If A gets a column resized, it will fire the event to B, but B will not fire the event to C. If C is also dependent on A, it needs to be set up directly. This stops cyclic dependencies between grids causing infinite firing of events if two grids are aligned to each other.
