---
product: "AG Grid"
title: "PDF Export - Master Detail"
description: "By default, exporting a Master Detail grid includes the master rows but not their detail grids. Use getCustomContentBelowRow to return a PDF representation of the detail data that should be inserted below each master row."
enterprise: true
framework: angular
version: "36.2.0"
related:
    - title: "Styles"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-styles/"
    - title: "Languages"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-languages/"
    - title: "Extra Content"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-extra-content/"
    - title: "Customising Content"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-customising-content/"
    - title: "Images"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-images/"
    - title: "Watermarks"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-watermarks/"
    - title: "Rows"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-rows/"
    - title: "Columns"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-columns/"
    - title: "Hyperlinks"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-hyperlinks/"
    - title: "Page Setup"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-page-setup/"
    - title: "API Reference"
      url: "https://www.ag-grid.com/angular-data-grid/pdf-export-api/"
llms: "https://www.ag-grid.com/llms.txt"
---

# PDF Export - Master Detail

By default, exporting a Master Detail grid includes the master rows but not their detail grids. Use `getCustomContentBelowRow` to return a PDF representation of the detail data that should be inserted below each master row.

## Exporting Master And Detail Rows

`getCustomContentBelowRow` receives the master row node and returns `PdfCell[][]`. Build these rows from the detail data stored on the master record:

```ts
<ag-grid-angular
    [defaultPdfExportParams]="defaultPdfExportParams"
    /* other grid options ... */ />

this.defaultPdfExportParams = {
    getCustomContentBelowRow: (params) => {
        const records = params.node.data.callRecords;

        return [
            [
                { data: { value: 'Call ID' } },
                { data: { value: 'Direction' } },
                { data: { value: 'Number' } },
                { data: { value: 'Duration / Switch Code' } },
            ],
            ...records.map((record) => [
                { data: { value: String(record.callId) } },
                { data: { value: record.direction } },
                { data: { value: record.number } },
                { data: { value: `${record.duration}s / ${record.switchCode}` } },
            ]),
        ];
    },
};
```

This approach does not create or export a separate detail-grid instance. It works whether the detail row is expanded or collapsed and avoids creating every detail grid during export.

Detail-grid behaviour is not applied automatically. If `detailGridOptions` contains Value Getters, Value Formatters, sorting or filtering that should be represented in the PDF, apply the equivalent logic while building the custom rows.

> **Note**
>
> Custom content uses the master table's exported column layout. Build a compact detail representation that fits the number and widths of the exported master columns.

The example below exports a heading, column headers and all call records below each master account row. The duration formatting used by the detail grid is reproduced when the PDF content is created.

#### PDF Export - Master Detail

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IDetailCellRendererParams,
  ModuleRegistry,
  PdfCell,
  PdfCellStyle,
  PdfExportParams,
  ProcessRowGroupForExportParams,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ContextMenuModule,
  MasterDetailModule,
  PdfExportModule,
} from "ag-grid-enterprise";

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

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  MasterDetailModule,
  PdfExportModule,
  ColumnMenuModule,
  ContextMenuModule,
]);
import { IAccount, ICallRecord } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="controls">
      <button (click)="onBtExport()">Export to PDF</button>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [masterDetail]="true"
      [detailCellRendererParams]="detailCellRendererParams"
      [defaultPdfExportParams]="defaultPdfExportParams"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IAccount>;

  columnDefs: ColDef[] = [
    { field: "name", cellRenderer: "agGroupCellRenderer", minWidth: 180 },
    { field: "account" },
    { field: "calls" },
    { field: "minutes", valueFormatter: (params) => `${params.value}m` },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 110,
  };
  detailCellRendererParams: any = {
    detailGridOptions: {
      columnDefs: [
        { field: "callId" },
        { field: "direction" },
        { field: "number", minWidth: 150 },
        { field: "duration", valueFormatter: (params) => `${params.value}s` },
        { field: "switchCode", minWidth: 150 },
      ],
      defaultColDef: {
        flex: 1,
      },
    },
    getDetailRowData: (params) => {
      params.successCallback(params.data.callRecords);
    },
  } as IDetailCellRendererParams<IAccount, ICallRecord>;
  defaultPdfExportParams: PdfExportParams = {
    columnWidth: "auto",
    getCustomContentBelowRow: getDetailRows,
  };
  rowData!: IAccount[];

  constructor(private http: HttpClient) {}

  onBtExport() {
    this.gridApi.exportDataAsPdf();
  }

  onGridReady(params: GridReadyEvent<IAccount>) {
    this.gridApi = params.api;

    this.http
      .get<
        IAccount[]
      >("https://www.ag-grid.com/example-assets/master-detail-data.json")
      .subscribe((data) => {
        this.rowData = data.slice(0, 3);
      });
  }
}

const detailHeadingStyle: PdfCellStyle = {
  backgroundColor: "#d9eaf7",
  color: "#19486a",
  fontWeight: "bold",
  padding: 6,
};
const detailHeaderStyle: PdfCellStyle = {
  backgroundColor: "#eef4f8",
  color: "#243746",
  fontWeight: "bold",
  padding: 5,
};
function getDetailRows(
  params: ProcessRowGroupForExportParams<IAccount>,
): PdfCell[][] {
  const account = params.node.data;
  if (!account) {
    return [];
  }
  const rows: PdfCell[][] = [
    [
      {
        data: { value: `Calls for ${account.name}` },
        mergeAcross: 3,
        style: detailHeadingStyle,
      },
    ],
    [
      { data: { value: "Call ID" }, style: detailHeaderStyle },
      { data: { value: "Direction" }, style: detailHeaderStyle },
      { data: { value: "Number" }, style: detailHeaderStyle },
      { data: { value: "Duration / Switch Code" }, style: detailHeaderStyle },
    ],
  ];
  for (const record of account.callRecords) {
    rows.push([
      { data: { value: String(record.callId) } },
      { data: { value: record.direction } },
      { data: { value: record.number } },
      { data: { value: `${record.duration}s / ${record.switchCode}` } },
    ]);
  }
  return rows;
}
```

[Live example: PDF Export - Master Detail](https://www.ag-grid.com/examples/pdf-export-master-detail/pdf-export-master-detail/angular/)

## API

### Export Options

See below the functions on the `PdfExportParams` interface to customise exported grid values.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getCustomContentBelowRow` | `Function` |  |  |  |

### PdfCell

Properties available on the `PdfCell` interface.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `data` | `PdfCellData` |  |  |  |
| `mergeAcross` | `number` |  |  |  |
| `style` | `PdfCellStyle` |  |  |  |

### PdfCellData

Properties available on the `PdfCellData` interface.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | `string \| null` |  |  |  |
| `hyperlink` | `string` |  |  |  |
| `image` | `PdfImage` |  |  |  |

### ProcessRowGroupForExportParams

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

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `node` | `IRowNode` |  |  |  |
| `column` | `Column` |  |  |  |
| `api` | `GridApi` |  |  |  |
| `context` | `TContext` |  |  |  |
