---
title: "Excel Export - Master Detail"
enterprise: true
framework: vue
version: "36.1.0"
---

# Excel Export - Master Detail

Excel Export provides ways to export Master/Detail grids to Excel.

## Exporting to a Single Sheet

By default, exporting the master grid will only export the master rows. If you want to include detail rows in the export, please use the `getCustomContentBelowRow` callback to generate a representation of the detail row that will be inserted below the master rows in the export.

There is an important difference between rendering and exporting Master / Detail content. When you expand a master row in the UI, a new instance of the Grid is created to render the detail, meaning that you have the full power of the Grid to sort, filter and format the detail data.

When exporting, the original data object representing the row is passed to `getCustomContentBelowRow` which returns styled content to be inserted into the export. In this case no separate instance of the Grid is created for the detail rows. This ensures good export performance even with large Master / Detail data sets. However, if your `detailGridOptions` contains value getters, value formatters, sorting, filtering etc and you want these to appear in the export, they must be applied inside `getCustomContentBelowRow`.

> **Note**
>
> Since detail grids are full Grid instances, triggering an export through the right-click context menu on a detail grid will do a normal export for the detail grid only. If this is not appropriate for your application you can disable the export item in the context menu, or replace it with a custom item that triggers an export on the master grid.

The example below demonstrates how both the master and detail data can be exported.

#### Exporting Master / Detail Data

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./style.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  CsvCell,
  CsvExportParams,
  ExcelCell,
  ExcelExportParams,
  ExcelRow,
  ExcelStyle,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IDetailCellRendererParams,
  ModuleRegistry,
  ProcessRowGroupForExportParams,
  enableDevValidations,
} from "ag-grid-community";
import {
  ClipboardModule,
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  ExcelExportModule,
  MasterDetailModule,
} from "ag-grid-enterprise";
import { IAccount } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnsToolPanelModule,
  ExcelExportModule,
  MasterDetailModule,
  ColumnMenuModule,
  ContextMenuModule,
]);

const getRows = (params: ProcessRowGroupForExportParams) => {
  const rows = [
    {
      outlineLevel: 1,
      cells: [
        cell(""),
        cell("Call Id", "header"),
        cell("Direction", "header"),
        cell("Number", "header"),
        cell("Duration", "header"),
        cell("Switch Code", "header"),
      ],
    },
  ].concat(
    ...params.node.data.callRecords.map((record: any) => [
      {
        outlineLevel: 1,
        cells: [
          cell(""),
          cell(record.callId, "body"),
          cell(record.direction, "body"),
          cell(record.number, "body"),
          cell(record.duration, "body"),
          cell(record.switchCode, "body"),
        ],
      },
    ]),
  );
  return rows;
};

function cell(text: string, styleId?: string): ExcelCell {
  return {
    styleId: styleId,
    data: {
      type: /^\d+$/.test(text) ? "Number" : "String",
      value: String(text),
    },
  };
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div class="container">
      <div>
        <button v-on:click="onBtExport()" style="margin-bottom: 5px; font-weight: bold">Export to Excel</button>
      </div>
      <div class="grid-wrapper">
        <ag-grid-vue
          style="width: 100%; height: 100%;"
          @grid-ready="onGridReady"
          :defaultCsvExportParams="defaultCsvExportParams"
          :defaultExcelExportParams="defaultExcelExportParams"
          :columnDefs="columnDefs"
          :defaultColDef="defaultColDef"
          :masterDetail="true"
          :detailCellRendererParams="detailCellRendererParams"
          :excelStyles="excelStyles"
          :rowData="rowData"></ag-grid-vue>
        </div>
      </div>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IAccount> | null>(null);
    const defaultCsvExportParams = ref<CsvExportParams>({
      getCustomContentBelowRow: (params) => {
        const rows = getRows(params);
        return rows.map((row) => row.cells) as CsvCell[][];
      },
    });
    const defaultExcelExportParams = ref<ExcelExportParams>({
      getCustomContentBelowRow: (params) => getRows(params) as ExcelRow[],
      columnWidth: 120,
      fileName: "ag-grid.xlsx",
    });
    const columnDefs = ref<ColDef[]>([
      // group cell renderer needed for expand / collapse icons
      { field: "name", cellRenderer: "agGroupCellRenderer" },
      { field: "account" },
      { field: "calls" },
      { field: "minutes", valueFormatter: "x.toLocaleString() + 'm'" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
    });
    const detailCellRendererParams = ref({
      detailGridOptions: {
        columnDefs: [
          { field: "callId" },
          { field: "direction" },
          { field: "number", minWidth: 150 },
          { field: "duration", valueFormatter: "x.toLocaleString() + 's'" },
          { field: "switchCode", minWidth: 150 },
        ],
        defaultColDef: {
          flex: 1,
        },
      },
      getDetailRowData: (params) => {
        params.successCallback(params.data.callRecords);
      },
    } as IDetailCellRendererParams<IAccount, ICallRecord>);
    const excelStyles = ref<ExcelStyle[]>([
      {
        id: "header",
        interior: {
          color: "#aaaaaa",
          pattern: "Solid",
        },
      },
      {
        id: "body",
        interior: {
          color: "#dddddd",
          pattern: "Solid",
        },
      },
    ]);
    const rowData = ref<IAccount[]>(null);

    function onBtExport() {
      gridApi.value!.exportDataAsExcel();
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

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

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

    return {
      gridApi,
      defaultCsvExportParams,
      defaultExcelExportParams,
      columnDefs,
      defaultColDef,
      detailCellRendererParams,
      excelStyles,
      rowData,
      onGridReady,
      onBtExport,
    };
  },
});

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

[Live example: Exporting Master / Detail Data](https://www.ag-grid.com/examples/excel-export-master-detail/single-sheet/vue3)

## Exporting to Multiple Sheets

Note the following:

- The `Master Detail` data is only available for `expanded` nodes, for more info see [Detail Grids](https://www.ag-grid.com/vue-data-grid/master-detail-grids/).
- The `RowBuffer` was set to **100** so all Detail Grids would be available.
- The `Detail Grids` get exported into different sheets.

Note the following:

- In this case we're not using the above approach with `getCustomContentBelowRow`, so the grid will be exported as-is. This means that detail data for a master-level row can be exported only if the master row is expanded. The `groupDefaultExpanded` property is set to 1 to expand the loaded master rows. For more information see [Detail Grids](https://www.ag-grid.com/vue-data-grid/master-detail-grids/).
- The `RowBuffer` property is set to 100 to ensure that at least 100 master rows are loaded, so they can be expanded. If you have more than 100 master-level rows, you'll need to set this value accordingly to cover all master rows, so they can be expanded and their detail data can be included in the export.
- Each Detail grid gets exported into a separate sheet in the Excel file

#### Excel Export - Multiple Sheets with Master Detail

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  FirstDataRenderedEvent,
  GetRowIdFunc,
  GetRowIdParams,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IDetailCellRendererParams,
  ModuleRegistry,
  RowApiModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  ClipboardModule,
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  ExcelExportModule,
  MasterDetailModule,
} from "ag-grid-enterprise";
import { IAccount } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowApiModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnsToolPanelModule,
  ExcelExportModule,
  MasterDetailModule,
  ColumnMenuModule,
  ContextMenuModule,
]);

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div class="container">
      <div>
        <button v-on:click="onBtExport()" style="margin-bottom: 5px; font-weight: bold">Export to Excel</button>
      </div>
      <div class="grid-wrapper">
        <ag-grid-vue
          style="width: 100%; height: 100%;"
          @grid-ready="onGridReady"
          :columnDefs="columnDefs"
          :defaultColDef="defaultColDef"
          :getRowId="getRowId"
          :groupDefaultExpanded="groupDefaultExpanded"
          :rowBuffer="rowBuffer"
          :masterDetail="true"
          :detailCellRendererParams="detailCellRendererParams"
          :rowData="rowData"
          @first-data-rendered="onFirstDataRendered"></ag-grid-vue>
        </div>
      </div>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IAccount> | null>(null);
    const columnDefs = ref<ColDef[]>([
      // group cell renderer needed for expand / collapse icons
      { field: "name", cellRenderer: "agGroupCellRenderer" },
      { field: "account" },
      { field: "calls" },
      { field: "minutes", valueFormatter: "x.toLocaleString() + 'm'" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
    });
    const getRowId = ref<GetRowIdFunc>((params: GetRowIdParams) => {
      return params.data.name;
    });
    const groupDefaultExpanded = ref(1);
    const rowBuffer = ref(100);
    const detailCellRendererParams = ref({
      detailGridOptions: {
        columnDefs: [
          { field: "callId" },
          { field: "direction" },
          { field: "number", minWidth: 150 },
          { field: "duration", valueFormatter: "x.toLocaleString() + 's'" },
          { field: "switchCode", minWidth: 150 },
        ],
        defaultColDef: {
          flex: 1,
        },
      },
      getDetailRowData: (params) => {
        params.successCallback(params.data.callRecords);
      },
    } as IDetailCellRendererParams<IAccount, ICallRecord>);
    const rowData = ref<IAccount[]>(null);

    function onFirstDataRendered(params: FirstDataRenderedEvent) {
      params.api.forEachNode(function (node) {
        node.setExpanded(true);
      });
    }
    function onBtExport() {
      const spreadsheets = [];
      const mainSheet = gridApi.value!.getSheetDataForExcel();
      if (mainSheet) {
        spreadsheets.push(mainSheet);
      }
      gridApi.value!.forEachDetailGridInfo(function (node) {
        const sheet = node.api!.getSheetDataForExcel({
          sheetName: node.id.replace("detail_", ""),
        });
        if (sheet) {
          spreadsheets.push(sheet);
        }
      });
      gridApi.value!.exportMultipleSheetsAsExcel({
        data: spreadsheets,
        fileName: "ag-grid.xlsx",
      });
    }
    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

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

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

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      getRowId,
      groupDefaultExpanded,
      rowBuffer,
      detailCellRendererParams,
      rowData,
      onGridReady,
      onFirstDataRendered,
      onBtExport,
    };
  },
});

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

[Live example: Excel Export - Multiple Sheets with Master Detail](https://www.ag-grid.com/examples/excel-export-master-detail/multiple-sheets/vue3)
