---
title: "Excel Export - Master Detail"
enterprise: true
framework: react
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

```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 "./style.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  CsvCell,
  CsvExportParams,
  ExcelCell,
  ExcelExportParams,
  ExcelRow,
  ExcelStyle,
  GridApi,
  GridOptions,
  IDetailCellRendererParams,
  ModuleRegistry,
  ProcessRowGroupForExportParams,
  enableDevValidations,
} from "ag-grid-community";
import {
  ClipboardModule,
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  ExcelExportModule,
  MasterDetailModule,
} from "ag-grid-enterprise";
import { IAccount } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  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;
};

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

const GridExample = () => {
  const gridRef = useRef<AgGridReact<IAccount>>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const defaultCsvExportParams = useMemo<CsvExportParams>(() => {
    return {
      getCustomContentBelowRow: (params) => {
        const rows = getRows(params);
        return rows.map((row) => row.cells) as CsvCell[][];
      },
    };
  }, []);
  const defaultExcelExportParams = useMemo<ExcelExportParams>(() => {
    return {
      getCustomContentBelowRow: (params) => getRows(params) as ExcelRow[],
      columnWidth: 120,
      fileName: "ag-grid.xlsx",
    };
  }, []);
  const [columnDefs, setColumnDefs] = useState<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 = useMemo<ColDef>(() => {
    return {
      flex: 1,
    };
  }, []);
  const detailCellRendererParams = useMemo(() => {
    return {
      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 = useMemo<ExcelStyle[]>(() => {
    return [
      {
        id: "header",
        interior: {
          color: "#aaaaaa",
          pattern: "Solid",
        },
      },
      {
        id: "body",
        interior: {
          color: "#dddddd",
          pattern: "Solid",
        },
      },
    ];
  }, []);

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

  const onBtExport = useCallback(() => {
    gridRef.current!.api.exportDataAsExcel();
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="container">
          <div>
            <button
              onClick={onBtExport}
              style={{ marginBottom: "5px", fontWeight: "bold" }}
            >
              Export to Excel
            </button>
          </div>
          <div className="grid-wrapper">
            <div style={gridStyle}>
              <AgGridReact<IAccount>
                ref={gridRef}
                rowData={data}
                loading={loading}
                defaultCsvExportParams={defaultCsvExportParams}
                defaultExcelExportParams={defaultExcelExportParams}
                columnDefs={columnDefs}
                defaultColDef={defaultColDef}
                masterDetail={true}
                detailCellRendererParams={detailCellRendererParams}
                excelStyles={excelStyles}
              />
            </div>
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

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

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

## 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/react-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/react-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

```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 {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  FirstDataRenderedEvent,
  GetRowIdFunc,
  GetRowIdParams,
  GridApi,
  GridOptions,
  IDetailCellRendererParams,
  ModuleRegistry,
  RowApiModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  ClipboardModule,
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  ExcelExportModule,
  MasterDetailModule,
} from "ag-grid-enterprise";
import { IAccount } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  RowApiModule,
  ClientSideRowModelModule,
  ClipboardModule,
  ColumnsToolPanelModule,
  ExcelExportModule,
  MasterDetailModule,
  ColumnMenuModule,
  ContextMenuModule,
];

const GridExample = () => {
  const gridRef = useRef<AgGridReact<IAccount>>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<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 = useMemo<ColDef>(() => {
    return {
      flex: 1,
    };
  }, []);
  const getRowId = useCallback((params: GetRowIdParams) => {
    return params.data.name;
  }, []);
  const detailCellRendererParams = useMemo(() => {
    return {
      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 { data, loading } = useFetchJson<IAccount>(
    "https://www.ag-grid.com/example-assets/master-detail-data.json",
  );

  const onFirstDataRendered = useCallback((params: FirstDataRenderedEvent) => {
    params.api.forEachNode(function (node) {
      node.setExpanded(true);
    });
  }, []);

  const onBtExport = useCallback(() => {
    const spreadsheets = [];
    const mainSheet = gridRef.current!.api.getSheetDataForExcel();
    if (mainSheet) {
      spreadsheets.push(mainSheet);
    }
    gridRef.current!.api.forEachDetailGridInfo(function (node) {
      const sheet = node.api!.getSheetDataForExcel({
        sheetName: node.id.replace("detail_", ""),
      });
      if (sheet) {
        spreadsheets.push(sheet);
      }
    });
    gridRef.current!.api.exportMultipleSheetsAsExcel({
      data: spreadsheets,
      fileName: "ag-grid.xlsx",
    });
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="container">
          <div>
            <button
              onClick={onBtExport}
              style={{ marginBottom: "5px", fontWeight: "bold" }}
            >
              Export to Excel
            </button>
          </div>
          <div className="grid-wrapper">
            <div style={gridStyle}>
              <AgGridReact<IAccount>
                ref={gridRef}
                rowData={data}
                loading={loading}
                columnDefs={columnDefs}
                defaultColDef={defaultColDef}
                getRowId={getRowId}
                groupDefaultExpanded={1}
                rowBuffer={100}
                masterDetail={true}
                detailCellRendererParams={detailCellRendererParams}
                onFirstDataRendered={onFirstDataRendered}
              />
            </div>
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

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

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