ScreenshotNeo

BlogHTML to image & PDF

Convert HTML to PDF with Headers and Footers in Playwright

Use Playwright’s Chromium PDF export to render HTML with repeating headers and footers. Learn templates, margins, print settings, and fixes for missing content.

By the ScreenshotNeo team4 October 20267 min read

Use Playwright’s Chromium page.pdf() method to convert a rendered HTML page to PDF. Set displayHeaderFooter: true and pass HTML strings in headerTemplate and footerTemplate. Add top and bottom margins so the repeating content has room. The method returns a PDF buffer; set path to save a file directly. See the Page API and PDF Export guide for version-specific details.

1. Complete Node.js example

This example loads an HTML file, exports it to A4 PDF, and adds the document title and date above the body plus page numbers below it. Install Playwright and its Chromium browser in your project before running it.

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage();
    await page.goto('file://' + require('path').resolve('input.html'), {
      waitUntil: 'networkidle',
    });

    await page.pdf({
      path: 'output.pdf',
      format: 'A4',
      displayHeaderFooter: true,
      headerTemplate: `
        <div style="width:100%; font-size:9px; padding:0 12mm; color:#444;">
          <span class="title"></span>
          <span style="float:right" class="date"></span>
        </div>`,
      footerTemplate: `
        <div style="width:100%; font-size:9px; text-align:center; color:#444;">
          <span class="pageNumber"></span> / <span class="totalPages"></span>
        </div>`,
      margin: {
        top: '18mm',
        bottom: '18mm',
        left: '12mm',
        right: '12mm',
      },
      printBackground: true,
    });
  } finally {
    await browser.close();
  }
})();

Save the code as make-pdf.js beside input.html, then run node make-pdf.js. page.pdf() resolves with a buffer as well as writing to output.pdf because path is set. Remove path when another part of your application will store or stream the returned buffer.

displayHeaderFooter defaults to false. Without explicitly enabling it, the templates are not shown. The templates are HTML strings, and Playwright supplies these special classes:

Class Value in the PDF
date Print date
title Document title
url Page URL
pageNumber Current page number
totalPages Total page count

For example, use <span class="url"></span> to print the URL or place Page <span class="pageNumber"></span> of <span class="totalPages"></span> in the footer. Scripts in template HTML do not execute, and styles from the page do not carry into the templates. Keep the markup self-contained and put its styling inline. Use simple HTML and CSS for predictable output.

3. Paper size, margins, and print styling

Choose settings based on how the document should paginate. Margins are particularly important: the PDF API defaults them to zero, so body content can overlap or crowd a header or footer unless you reserve space.

Option Behavior and when to use it
format Paper preset such as A4 or Letter. If supplied, it takes precedence over width and height.
width, height Set a custom paper size when a preset does not fit.
landscape Use landscape orientation for wide content.
margin Set top, bottom, left, and right. Values accept units; bare numbers mean pixels. Leave enough top and bottom room for the templates.
preferCSSPageSize Set to true when the document’s CSS @page size should take precedence. Otherwise, the selected paper size is used and content may be scaled to fit.
scale Scale printed content; default is 1, and documented values range from 0.1 to 2.
pageRanges Restrict output to selected pages when only part of a long document is needed.

Playwright uses print CSS media by default. If the layout intentionally depends on screen styles, call await page.emulateMedia({ media: 'screen' }) before page.pdf(). Print rendering can adjust colors; use -webkit-print-color-adjust in your print CSS when exact colors matter. Background graphics are off by default, so set printBackground: true if the design relies on background fills or images.

4. HTML input and readiness

For a local file, navigate to its file URL as in the example. For a web page, use page.goto('https://example.com', { waitUntil: 'networkidle' }) when network activity should settle before export. For HTML held in a string, use await page.setContent(html, { waitUntil: 'networkidle' }). If fonts, images, or application data appear after that point, wait for the relevant selector or readiness signal before printing; do not assume that a successful navigation means every custom asynchronous component has finished rendering.

PDF export is Chromium-only in Playwright. Launch Chromium for this workflow; the same PDF export is not available through Firefox or WebKit.

5. cURL, Python, and Node.js alternatives for screenshot output

If the requirement is a PDF with repeating print headers and footers, use Playwright’s PDF API above: a screenshot endpoint produces an image or PDF, but does not expose Playwright’s HTML header and footer templates. For a visual capture of a web page rather than a paginated document, these runnable request patterns show the corresponding ScreenshotNeo API call. Replace the placeholder key and target URL as appropriate. See the ScreenshotNeo API documentation for available parameters and response details.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

These examples capture a page image using the supplied API call pattern; they do not create a Playwright-style PDF with repeating headers and footers. ScreenshotNeo is a website screenshot API and MCP server. It can accept cookie banners like a visitor and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture, with each step configurable. It bills only clean shots: bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with X-Page-Verdict and X-Billed response headers indicating the outcome. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for MCP clients including Claude and Cursor. Plans include 1,000 shots a month free without a card; paid plans start at $5 for 3,000 shots. See ScreenshotNeo for product details.

6. Troubleshooting

Symptom Likely cause Fix
No header or footer appears displayHeaderFooter was omitted or is false. Set displayHeaderFooter: true and confirm the template string is nonempty.
Header or footer is clipped or body text overlaps it Top or bottom margins are zero or too small. Increase the corresponding margin and keep the template content within the printable area.
Template looks unstyled Page styles do not apply inside template HTML. Put the necessary styles directly in the template, typically as inline styles.
Template script did not run Scripts inside header/footer templates are not evaluated. Use the built-in template classes for date, title, URL, page number, and page count; calculate any other content in application code before building the template.
Colors or background artwork are missing Background printing defaults off, and print rendering may adjust colors. Set printBackground: true; add -webkit-print-color-adjust: exact to the relevant print CSS if exact colors are needed.
PDF uses an unexpected layout PDF export uses print media by default, or CSS @page and the selected paper size conflict. Emulate screen media only if intended. Choose whether format or CSS page sizing should control the output using preferCSSPageSize.
PDF export fails in Firefox or WebKit Playwright PDF generation is Chromium-only. Launch Chromium for this export path.
Images, fonts, or dynamic content are absent The page was printed before the resources or application content were ready. Wait for the required selector or app-specific readiness signal; verify external resources are reachable from the browser environment.

7. Performance, reliability, and cost

PDF generation renders and paginates the page in Chromium, so document complexity and resource loading affect completion time and output size. Keep the page’s inputs deterministic, wait only for the resources the document needs, and choose whether to return the buffer or write via path. For repeated jobs, manage browser lifetime deliberately and close pages and browsers when finished. Test representative documents for page breaks, font availability, and image loading in the Chromium version deployed by your project.

Playwright is the browser automation library in this workflow; the cited API documentation does not state a per-document service price. Your operational cost depends on where and how you run Chromium. ScreenshotNeo’s API has a separate published plan structure: 1,000 screenshots per month free with no card, then $5 for 3,000 on Starter, $15 for 15,000 on Growth, $39 for 60,000 on Pro, $99 for 250,000 on Scale, or $249 for 1,000,000 on Business. Yearly billing gives two months free, and every feature is on every plan. Those screenshot plans do not change the Playwright template API behavior.

8. FAQ

Can the header show the page title automatically?

Yes. Put an element with class title in either template.

Can I use a custom header on only the first page?

The documented template options repeat the supplied header and footer; conditional per-page template logic is not provided by these options.

Does page.pdf() return bytes if I set path?

Yes. It returns a PDF buffer and can also write that PDF to the requested path.

Can I use page CSS to style the repeating header?

No. Template styles are separate from the page’s styles, so include template styling in the template itself.

9. Or skip the browser setup

For screenshots or a PDF capture through an API, make one request to ScreenshotNeo. This does not replace Playwright’s print header and footer templates.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. 1,000 screenshots a month are free with no card, and paid plans start at $5 for 3,000. Read the API docs and product overview, then sign up for the free plan.