Guides

Add page numbers, headers and footers to an HTML-generated PDF

Chrome's PDF engine (used by Puppeteer, Playwright and PDFLoom) can print a header and footer on every page and fill in the page number. It works well once you know three quirks.

The basic template

{
  "html": "<h1>Quarterly report</h1>...",
  "displayHeaderFooter": true,
  "margin": { "top": "25mm", "bottom": "20mm", "left": "15mm", "right": "15mm" },
  "headerTemplate": "<div style='font-size:9px; width:100%; padding:0 15mm; color:#666'>ACME Corp, Quarterly report</div>",
  "footerTemplate": "<div style='font-size:9px; width:100%; text-align:center; color:#666'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>"
}

Chrome fills these classes in automatically:

Quirk 1: the header or footer is invisible

Headers and footers are drawn inside the page margin. With zero margin there's no room for them. Set margin.top and margin.bottom to at least 15–20mm.

Quirk 2: the text is tiny

The default font size in header and footer templates is very small. Always set font-size explicitly, around 8–10px.

Quirk 3: no external CSS or images

Templates are rendered separately from the page. Stylesheets from your document don't apply, and external images won't load. Use inline styles, and embed a logo as a base64 data URI:

<div style="width:100%; padding:0 15mm; display:flex; justify-content:space-between; font-size:9px">
  <img src="data:image/png;base64,iVBORw0..." style="height:14px">
  <span class="date"></span>
</div>

Background colors in templates also need -webkit-print-color-adjust: exact.

Puppeteer version

await page.pdf({
  format: "A4",
  displayHeaderFooter: true,
  margin: { top: "25mm", bottom: "20mm" },
  headerTemplate: "<span></span>",
  footerTemplate: `<div style="font-size:9px;width:100%;text-align:center">
    <span class="pageNumber"></span> / <span class="totalPages"></span></div>`,
});

Pass headerTemplate: "<span></span>" if you only want a footer. Otherwise Chrome prints its default header with the date and title.

Skipping the number on the cover page

Templates are the same on every page, so you can't hide them on page 1. Two common workarounds: render the cover separately and merge the PDFs, or render all pages and use pageRanges to produce the parts you need.

Try it without installing anything

Every PDFLoom plan supports header and footer templates. Get a free API key, or see the API docs.


More guides