PDF Export can add document headings, page headers and footers, cover pages, and content before, after, or within the exported table.
Document Headings Copy Link
Use documentTitle to set the PDF metadata title and render a visible title above the table. Use documentTitleStyle to configure its font, colour, border, padding, margin, alignment, wrapping, line height, line limit, and overflow behaviour.
"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,
GridApi,
GridOptions,
ModuleRegistry,
NumberFilterModule,
PdfExportParams,
TextFilterModule,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ContextMenuModule,
PdfExportModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [
TextFilterModule,
NumberFilterModule,
ClientSideRowModelModule,
PdfExportModule,
ColumnMenuModule,
ContextMenuModule,
];
const GridExample = () => {
const gridRef = useRef<AgGridReact<IOlympicData>>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<(ColDef | ColGroupDef)[]>([
{
headerName: "Group A",
children: [
{ field: "athlete", minWidth: 200 },
{ field: "country", minWidth: 200 },
],
},
{
headerName: "Group B",
children: [
{ field: "sport", minWidth: 150 },
{ field: "gold" },
{ field: "silver" },
{ field: "bronze" },
{ field: "total" },
],
},
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
filter: true,
minWidth: 100,
flex: 1,
};
}, []);
const defaultPdfExportParams = useMemo<PdfExportParams>(() => {
return {
documentTitle: "Quarterly Results",
documentTitleStyle: {
fontSize: 16,
padding: 6,
margin: { bottom: 10 },
backgroundColor: "#f3f6fb",
borderColor: "#c3d4ea",
borderWidth: 1,
color: "#123a5a",
alignment: "center",
},
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/small-olympic-winners.json",
);
const onBtExport = useCallback(() => {
gridRef.current!.api.exportDataAsPdf();
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="container">
<div>
<button
onClick={onBtExport}
style={{ marginBottom: "5px", fontWeight: "bold" }}
>
Export PDF
</button>
</div>
<div className="grid-wrapper">
<div style={gridStyle}>
<AgGridReact<IOlympicData>
ref={gridRef}
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
defaultPdfExportParams={defaultPdfExportParams}
/>
</div>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.details > label {
margin-bottom: 10px;
}
.details > label:first-of-type {
margin-top: 10px;
}
.details > label:last-of-type {
margin-bottom: 0;
}
.option {
display: block;
margin: 5px 10px 5px 0;
}
.grid-wrapper {
display: flex;
flex: 1 1 0px;
}
.grid-wrapper > div {
width: 100%;
height: 100%;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
}
.columns {
display: flex;
flex-direction: row;
gap: 16px;
}
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; Subtitle Copy Link
Use documentSubtitle to render a subtitle below the title. It uses a smaller default font and can be styled independently with documentSubtitleStyle.
const documentTitle = 'Quarterly Results';
const documentSubtitle = 'Prepared for the board';
<AgGridReact
documentTitle={documentTitle}
documentSubtitle={documentSubtitle}
/>"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,
GridApi,
GridOptions,
ModuleRegistry,
PdfExportParams,
enableDevValidations,
} from "ag-grid-community";
import { ContextMenuModule, PdfExportModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
const modules = [ClientSideRowModelModule, PdfExportModule, ContextMenuModule];
interface ReportRow {
department: string;
owner: string;
result: number;
}
const GridExample = () => {
const gridRef = useRef<AgGridReact<ReportRow>>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [rowData, setRowData] = useState<ReportRow[]>([
{ department: "Engineering", owner: "Maya Singh", result: 94 },
{ department: "Operations", owner: "Daniel Price", result: 88 },
{ department: "Sales", owner: "Sofia Costa", result: 91 },
{ department: "Support", owner: "Noah Williams", result: 96 },
]);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "department", flex: 1 },
{ field: "owner", flex: 1 },
{ field: "result", headerName: "Result (%)", flex: 1 },
]);
const defaultPdfExportParams = useMemo<PdfExportParams>(() => {
return {
documentTitle: "Quarterly Results",
documentSubtitle: "Prepared for the board",
documentSubtitleStyle: {
color: "#52606d",
fontSize: 12,
},
};
}, []);
const onBtExport = useCallback(() => {
gridRef.current!.api.exportDataAsPdf();
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="container">
<button onClick={onBtExport}>Export PDF</button>
<div className="grid-wrapper">
<div style={gridStyle}>
<AgGridReact<ReportRow>
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultPdfExportParams={defaultPdfExportParams}
/>
</div>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.container {
display: flex;
flex-direction: column;
height: 100%;
gap: 8px;
}
.container > button {
align-self: flex-start;
font-weight: bold;
}
.grid-wrapper {
flex: 1 1 0;
}
#myGrid {
width: 100%;
height: 100%;
}
Page Headers And Footers Copy Link
Use headerFooterConfig to add content to the left, centre, or right of each page header and footer. The all rule applies by default, while first and even replace it on the corresponding pages.
Each header or footer accepts up to three entries. When position is omitted, entries are positioned left, centre, and right in array order.
Each position uses one third of the printable page width. Header and footer text remains on one line and is truncated with an ellipsis when it exceeds that space. The required vertical space is reserved before the table is paginated.
Page Headers Copy Link
const headerFooterConfig = {
all: {
header: [{ value: 'Quarterly Results', position: 'Center' }],
},
first: {
header: [{ value: 'Confidential Report', position: 'Center' }],
},
};
<AgGridReact headerFooterConfig={headerFooterConfig} />"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,
GridApi,
GridOptions,
ModuleRegistry,
PdfExportParams,
enableDevValidations,
} from "ag-grid-community";
import { ContextMenuModule, PdfExportModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
const modules = [ClientSideRowModelModule, PdfExportModule, ContextMenuModule];
interface ReportRow {
item: string;
owner: string;
status: string;
}
const GridExample = () => {
const gridRef = useRef<AgGridReact<ReportRow>>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [rowData, setRowData] = useState<ReportRow[]>(
Array.from({ length: 60 }, (_, index) => ({
item: `Work item ${index + 1}`,
owner: ["Amelia", "Mateo", "Hana"][index % 3],
status: index % 4 === 0 ? "In review" : "Complete",
})),
);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "item", flex: 1 },
{ field: "owner", flex: 1 },
{ field: "status", flex: 1 },
]);
const defaultPdfExportParams = useMemo<PdfExportParams>(() => {
return {
page: {
orientation: "portrait",
},
headerFooterConfig: {
all: {
header: [
{
value: "Quarterly Results",
position: "Center",
style: { color: "#123a5a", fontWeight: "bold" },
},
],
},
first: {
header: [
{
value: "Confidential Report",
position: "Center",
style: { color: "#8b1d1d", fontWeight: "bold" },
},
],
},
},
};
}, []);
const onBtExport = useCallback(() => {
gridRef.current!.api.exportDataAsPdf();
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="container">
<button onClick={onBtExport}>Export PDF</button>
<div className="grid-wrapper">
<div style={gridStyle}>
<AgGridReact<ReportRow>
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultPdfExportParams={defaultPdfExportParams}
/>
</div>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.container {
display: flex;
flex-direction: column;
height: 100%;
gap: 8px;
}
.container > button {
align-self: flex-start;
font-weight: bold;
}
.grid-wrapper {
flex: 1 1 0;
}
#myGrid {
width: 100%;
height: 100%;
}
Page Footers Copy Link
Header and footer values support the following placeholders:
&[Page]: current page number.&[Pages]: total number of pages.&[Date]: date when the PDF export started.&[Time]: time when the PDF export started.
Date and time are captured once per export and formatted using the export language, when provided.
const headerFooterConfig = {
all: {
footer: [
{ value: '&[Date]', position: 'Left' },
{ value: 'Page &[Page] of &[Pages]', position: 'Center' },
{ value: '&[Time]', position: 'Right' },
],
},
};
<AgGridReact headerFooterConfig={headerFooterConfig} /> Cover Page Copy Link
Set coverPage=true to place the document title and subtitle on the first page and begin the exported grid on the following page. Page header and footer rules still apply, so headerFooterConfig.first can customise the cover page.
const coverPage = true;
const documentTitle = 'Annual Performance Report';
const documentSubtitle = 'Financial year 2026';
<AgGridReact
coverPage={coverPage}
documentTitle={documentTitle}
documentSubtitle={documentSubtitle}
/>"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,
GridApi,
GridOptions,
ModuleRegistry,
PdfExportParams,
enableDevValidations,
} from "ag-grid-community";
import { ContextMenuModule, PdfExportModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
const modules = [ClientSideRowModelModule, PdfExportModule, ContextMenuModule];
interface ReportRow {
department: string;
owner: string;
result: number;
}
const GridExample = () => {
const gridRef = useRef<AgGridReact<ReportRow>>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [rowData, setRowData] = useState<ReportRow[]>([
{ department: "Engineering", owner: "Maya Singh", result: 94 },
{ department: "Operations", owner: "Daniel Price", result: 88 },
{ department: "Sales", owner: "Sofia Costa", result: 91 },
{ department: "Support", owner: "Noah Williams", result: 96 },
]);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "department", flex: 1 },
{ field: "owner", flex: 1 },
{ field: "result", headerName: "Result (%)", flex: 1 },
]);
const defaultPdfExportParams = useMemo<PdfExportParams>(() => {
return {
coverPage: true,
documentTitle: "Annual Performance Report",
documentTitleStyle: {
fontSize: 24,
margin: { top: 120, bottom: 8 },
borderColor: "#123a5a",
borderWidth: 1,
color: "#123a5a",
},
documentSubtitle: "Financial year 2026",
documentSubtitleStyle: {
color: "#52606d",
fontSize: 14,
},
headerFooterConfig: {
all: {
footer: [{ value: "Page &[Page] of &[Pages]", position: "Center" }],
},
first: {
footer: [
{
value: "Confidential",
position: "Center",
style: { color: "#8b1d1d" },
},
],
},
},
};
}, []);
const onBtExport = useCallback(() => {
gridRef.current!.api.exportDataAsPdf();
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="container">
<button onClick={onBtExport}>Export PDF</button>
<div className="grid-wrapper">
<div style={gridStyle}>
<AgGridReact<ReportRow>
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultPdfExportParams={defaultPdfExportParams}
/>
</div>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.container {
display: flex;
flex-direction: column;
height: 100%;
gap: 8px;
}
.container > button {
align-self: flex-start;
font-weight: bold;
}
.grid-wrapper {
flex: 1 1 0;
}
#myGrid {
width: 100%;
height: 100%;
}
Additional Content Copy Link
Use prependContent, appendContent, or getCustomContentBelowRow to add content that is not displayed in the grid. Strings create full-width rows. Use PdfCell[][] for explicit spans and styling.
See PDF Export - Master Detail for an example that uses getCustomContentBelowRow to include detail data below each master row.
"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,
GridApi,
GridOptions,
ModuleRegistry,
NumberFilterModule,
PdfExportParams,
ProcessRowGroupForExportParams,
TextFilterModule,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ContextMenuModule,
PdfExportModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [
TextFilterModule,
NumberFilterModule,
ClientSideRowModelModule,
PdfExportModule,
ColumnMenuModule,
ContextMenuModule,
];
const GridExample = () => {
const gridRef = useRef<AgGridReact<IOlympicData>>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "athlete", minWidth: 200 },
{ field: "country", minWidth: 160 },
{ field: "sport", minWidth: 140 },
{ field: "total" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
filter: true,
minWidth: 100,
flex: 1,
};
}, []);
const defaultPdfExportParams = useMemo<PdfExportParams>(() => {
return {
getCustomContentBelowRow: (params: ProcessRowGroupForExportParams) => {
const rowIndex = params.node.rowIndex ?? 0;
if ((rowIndex + 1) % 5 !== 0) {
return;
}
return [
[
{
data: { value: "Section break" },
mergeAcross: 3,
style: {
backgroundColor: "#fff4cc",
borderColor: "#f0c36d",
borderWidth: 1,
color: "#7a5400",
padding: 6,
alignment: "center",
},
},
],
];
},
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/small-olympic-winners.json",
);
const onBtExport = useCallback(() => {
gridRef.current!.api.exportDataAsPdf();
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="container">
<div>
<button
onClick={onBtExport}
style={{ marginBottom: "5px", fontWeight: "bold" }}
>
Export PDF
</button>
</div>
<div className="grid-wrapper">
<div style={gridStyle}>
<AgGridReact<IOlympicData>
ref={gridRef}
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
defaultPdfExportParams={defaultPdfExportParams}
/>
</div>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.details > label {
margin-bottom: 10px;
}
.details > label:first-of-type {
margin-top: 10px;
}
.details > label:last-of-type {
margin-bottom: 0;
}
.option {
display: block;
margin: 5px 10px 5px 0;
}
.grid-wrapper {
display: flex;
flex: 1 1 0px;
}
.grid-wrapper > div {
width: 100%;
height: 100%;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
}
.columns {
display: flex;
flex-direction: row;
gap: 16px;
}
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; API Copy Link
Export Options Copy Link
The document title stored in the PDF metadata. When set, a visible title is rendered above the exported table.
|
Styling for the visible document title.
|
A visible subtitle rendered below the document title.
|
Styling for the visible document subtitle.
|
Set to true to render the document title and subtitle on a separate first page. The exported grid begins on the following page. |
Page header and footer content.
|
Content to put at the top of the exported sheet.
|
Content to put at the bottom of the exported sheet.
|
A callback function to return content to be inserted below a row in the export. |
PdfDocumentHeadingStyle Copy Link
Properties available on the PdfDocumentHeadingStyle interface.
Margin around the document heading in points. A number applies to all sides.
|
Background colour.
|
Border colour.
|
Border width in points. Defaults to 1 when borderColor is set, otherwise 0.
|
Padding inside the cell in points. A number applies to all sides.
|
Horizontal alignment for the cell text.
|
Whether text should wrap onto multiple lines. Wrapped content increases the row height as required. |
Whether explicit line breaks should be preserved. |
Whether repeated, leading and trailing spaces should be preserved when text wraps. |
Maximum number of rendered text lines.
|
How text exceeding the available width, height or line limit is indicated. |
Font size in points.
|
Font family.
|
Font weight. When omitted, the weight from the resolved font family is preserved.
|
Font style. |
Text direction. auto uses the first strong directional character. When omitted, the export-level direction is used. Text direction does not change exported column order.
|
BCP 47 language tag used when selecting language-specific OpenType features. When omitted, the export-level language is used.
|
Text colour.
|
Distance between text baselines in points. Defaults to the natural line height from the resolved font metrics, with a minimum of fontSize.
|
PdfHeaderFooterConfig Copy Link
Properties available on the PdfHeaderFooterConfig interface.
Header and footer configuration applied to every page unless overridden. |
Header and footer configuration applied to the first page. |
Header and footer configuration applied to even-numbered pages. |
PdfHeaderFooter Copy Link
Properties available on the PdfHeaderFooter interface.
Up to three header entries positioned left, centre, and right. |
Up to three footer entries positioned left, centre, and right. |
PdfHeaderFooterTextContent Copy Link
Properties available on the PdfHeaderFooterTextContent interface.
Header or footer text. Supports &[Page], &[Pages], &[Date], and &[Time] placeholders.
|
Image rendered alongside the text. |
Position of the content within the printable page width. When omitted, array entries default to left, centre, and right in order.
|
Text styling for this entry. |
PdfHeaderFooterImageContent Copy Link
Properties available on the PdfHeaderFooterImageContent interface.
Header or footer text. Supports &[Page], &[Pages], &[Date], and &[Time] placeholders.
|
Image rendered alongside the text. |
Position of the content within the printable page width. When omitted, array entries default to left, centre, and right in order.
|
Text styling for this entry. |
PdfTextStyle Copy Link
Properties available on the PdfTextStyle interface.
Font size in points.
|
Font family.
|
Font weight. When omitted, the weight from the resolved font family is preserved.
|
Font style. |
Text direction. auto uses the first strong directional character. When omitted, the export-level direction is used. Text direction does not change exported column order.
|
BCP 47 language tag used when selecting language-specific OpenType features. When omitted, the export-level language is used.
|
Text colour.
|
Distance between text baselines in points. Defaults to the natural line height from the resolved font metrics, with a minimum of fontSize.
|
PdfCell Copy Link
Properties available on the PdfCell interface.
The data that will be added to the cell. |
The number of cells to span across (1 means span 2 columns). |
Optional styling for the cell.
|
PdfCellData Copy Link
Properties available on the PdfCellData interface.
The value of the cell. |
External URI opened when the exported cell text is selected. |
Image rendered alongside the cell value. |