Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a Puppeteer PDF, the most compatible way to print “Page 1 of 8” is to enable displayHeaderFooter and put pageNumber and totalPages spans in footerTemplate. CSS @page margin-box counters are a separate option: they can place generated content in page margins in Chrome 131 and later, but support depends on the Chromium build that Puppeteer actually launches.

The reliable Puppeteer method: footerTemplate

Puppeteer’s Page.pdf() API recognizes a small set of classes in header and footer templates. Add displayHeaderFooter: true, reserve space with a bottom margin, and include elements whose classes are pageNumber and totalPages. Puppeteer fills those elements with the current page and document page count.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

await page.setContent(`
  <main>
    <h1>Quarterly report</h1>
    <p>Your report content goes here. Add enough content to create multiple pages.</p>
  </main>
`, { waitUntil: 'networkidle0' });

await page.pdf({
  path: 'report.pdf',
  format: 'A4',
  printBackground: true,
  displayHeaderFooter: true,
  margin: {
    top: '16mm',
    right: '14mm',
    bottom: '18mm',
    left: '14mm'
  },
  footerTemplate: `
    <div style="width:100%; font-size:9px; text-align:right; padding:0 12mm;">
      <span class="pageNumber"></span> / <span class="totalPages"></span>
    </div>
  `
});

await browser.close();

The template is self-contained HTML. Inline styles are the safest choice because the footer is rendered separately from the page document’s normal stylesheet. The bottom margin must be large enough for the footer; otherwise text can overlap the body or be clipped.

What the injected classes mean

  • pageNumber displays the number of the page currently being rendered.
  • totalPages displays the total number of pages in the generated PDF.
  • Other markup in the template is ordinary HTML, so you can add a title, a rule, an image, or alignment wrappers.

Common footer layouts

footerTemplate: `
  <div style="width:100%; font:9px Arial, sans-serif; display:flex; justify-content:space-between; padding:0 10mm;">
    <span>Confidential</span>
    <span>Page <span class="pageNumber"></span> of <span class="totalPages"></span></span>
  </div>
`

Keep the footer’s height predictable. Large fonts, wrapping text, or external assets can change the available layout and make pagination appear different from the screen preview.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

How PDF media and margins affect the result

Page.pdf() renders with the print CSS media type by default. If your design is written for screen media, select it explicitly before generating the PDF:

await page.emulateMediaType('screen');
await page.pdf({
  displayHeaderFooter: true,
  footerTemplate: '<div>Page <span class="pageNumber"></span></div>',
  margin: { bottom: '18mm' }
});

Use this only when screen styling is intentional. Otherwise, leave the default print media behavior in place and define print-specific rules with @media print.

Reserve space and control the paper size

  • Set a bottom PDF margin for the footer. The margin is measured outside the document content area.
  • Use format: 'A4', format: 'Letter', or explicit width and height as appropriate for your audience.
  • If your CSS contains an @page { size: ... } declaration, set preferCSSPageSize: true when that CSS size should override Puppeteer’s format, width, or height options. Its documented default is false.
await page.pdf({
  path: 'custom-size.pdf',
  displayHeaderFooter: true,
  preferCSSPageSize: true,
  margin: { top: '15mm', right: '15mm', bottom: '20mm', left: '15mm' },
  footerTemplate: '<div style="text-align:center; width:100%; font-size:9px;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>'
});

Using CSS counters in @page margin boxes

CSS Paged Media defines page-margin boxes and page counters. In a browser that implements them, you can write the footer in CSS rather than Puppeteer’s template:

@page {
  size: A4;
  margin: 16mm 14mm 20mm;

  @bottom-center {
    content: "Page " counter(page) " of " counter(pages);
    font-size: 9pt;
    font-family: Arial, sans-serif;
  }
}

@media print {
  body { margin: 0; }
}

counter(page) represents the current page, while counter(pages) represents the total page count. This is not the same mechanism as footerTemplate: it is CSS-generated content in a page-margin box, whereas Puppeteer’s placeholders are elements in an HTML template.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.

Check the Chromium version before depending on margin boxes

Chrome for Developers documents generated content in print margins beginning with Chrome 131. Puppeteer can use a bundled browser or a browser executable selected by your deployment, so the Puppeteer package version alone does not prove that margin boxes are available. Confirm the actual Chromium version in CI and production, and render a representative multi-page document before adopting this approach.

If you need broad compatibility across older or mixed browser builds, use displayHeaderFooter with footerTemplate. If CSS-defined placement is a hard requirement, pin and verify a Chromium version that supports the margin-box feature.

Choosing between the two techniques

Requirement Recommended technique Reason
Works across more Puppeteer deployments footerTemplate It is the documented Puppeteer API and uses recognized placeholders.
Footer HTML needs custom layout footerTemplate You can provide a self-contained HTML fragment with inline styles.
Footer must be declared entirely in CSS @page margin boxes CSS controls the margin position and generated content.
Browser versions are not tightly controlled footerTemplate Margin-box behavior is dependent on the Chromium feature implementation.
CSS @page sets the paper size Either, plus preferCSSPageSize: true when needed This option gives CSS page size priority over PDF size options.

Validation checklist before shipping

  1. Generate a document with at least three pages, including a final page with little content.
  2. Confirm the first page shows 1 and the last page’s total matches the PDF’s actual page count.
  3. Inspect the bottom edge at normal and high zoom for clipping, overlap, and unexpectedly small text.
  4. Test long titles, narrow paper sizes, large fonts, tables, images, and pages created by forced breaks.
  5. Run the same code with the Chromium executable used in production; browser upgrades can alter pagination and CSS support.
  6. If the body overlaps the footer, increase the PDF bottom margin rather than adding arbitrary padding to the document.

Troubleshooting page numbers and CSS counters

The footer is missing

Ensure displayHeaderFooter: true is present in the same page.pdf() call as footerTemplate. The documented default is false. Also verify that the template is a non-empty HTML string.

The classes print literally

pageNumber and totalPages are recognized class names, not CSS counters. They must appear as class attributes on elements in footerTemplate, for example <span class="pageNumber"></span>. A typo, different capitalization, or putting the class only in the main document will not work.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

Numbers are clipped or overlap content

Increase margin.bottom, reduce footer font size, and keep the footer’s line height and padding predictable. Remember that footer space is not automatically reserved just because a template exists.

counter(page) stays empty

Your Chromium build may not implement CSS page-margin boxes, or the syntax may not be accepted in that build. Use the Puppeteer template method, or verify that the deployed browser meets the documented Chrome 131 threshold and that your CSS uses an @bottom-* margin box.

The page count changes after adding a footer

Pagination is sensitive to available space. A footer margin, different media type, font loading, or paper size can push content onto another page. Treat the final PDF—not a screen screenshot—as the source of truth, and test with production fonts and assets.

CSS page size is ignored

Set preferCSSPageSize: true when the @page size should win. With the default of false, Puppeteer’s explicit format, width, or height can take precedence.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft

Performance and reliability considerations

  • Wait for the content that determines pagination. Use page.setContent() with an appropriate waitUntil value, and await critical font or image loads before calling page.pdf().
  • Keep header and footer markup lightweight. External network requests in a footer can delay rendering or fail in restricted environments.
  • Pin or explicitly select the Chromium executable in production so CSS feature behavior is reproducible.
  • Store a sample PDF from each deployment and compare page count, footer position, and typography after Puppeteer or browser upgrades.
  • Do not infer compatibility from a single successful local render; container images, operating-system fonts, and browser channels can differ.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a clean screenshot or PDF capture rather than a custom Puppeteer footer, ScreenshotNeo provides a website screenshot API and MCP server. A single request can capture a URL, while its cleanup steps accept cookie and consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing result.

For an image capture, the documented cURL pattern is:

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

See the ScreenshotNeo API documentation for PDF options and the other capture parameters. The service also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. It supports full-page and element captures, device and viewport settings, dark mode, retina scale, custom CSS and JavaScript, click and wait actions, request blocking, cookies and headers, timezone and geolocation, caching, signed links, asynchronous webhooks, bulk capture, usage reporting, and HTML/CSS-to-image workflows.

Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account to try it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

FAQ

Can I put a custom prefix such as “Page” in the Puppeteer footer?

Yes. Put the text beside the recognized spans in footerTemplate, such as Page <span class="pageNumber"></span>. Puppeteer supplies only the numeric values.

Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.

Does totalPages require a second PDF pass?

No. Puppeteer injects the total page count while rendering the PDF. A second pass is unnecessary for the documented template placeholders.

Should I use counter(pages) or totalPages?

Use totalPages in the Puppeteer footer template. Use counter(pages) only when you intentionally rely on CSS page-margin boxes and have verified browser support.

Why does a footer look different from the page’s CSS?

The footer template is a separate HTML fragment. Put its essential styles inline instead of assuming the main document’s stylesheets will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I put a custom prefix such as “Page” in the Puppeteer footer?

Yes. Put the text beside the recognized spans in footerTemplate, such as Page <span class="pageNumber"></span>. Puppeteer supplies only the numeric values.

Does totalPages require a second PDF pass?

No. Puppeteer injects the total page count while rendering the PDF.

Should I use counter(pages) or totalPages?

Use totalPages in the Puppeteer footer template unless you have verified CSS page-margin-box support in your Chromium build.

The Bottom Line

For dependable page numbers, use displayHeaderFooter, a self-contained footerTemplate, and a sufficient bottom margin. Reserve CSS @page counters for deployments where the actual Chromium version has verified margin-box support.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.