Blog

HTML Print Pagination & Footer: 6 Approaches Compared

Adding page numbers and footers to PDF documents is one of the most common challenges when converting HTML to PDF. Unlike web pages that scroll infinitely, PDF documents are paginated โ€” and users expect professional documents to include page numbers, total page counts, and footer information on every page. However, HTML and CSS were not originally designed with print pagination in mind, which makes this surprisingly difficult.

In this comprehensive guide, we'll explore 6 different approaches to implementing page numbers and footers in HTML-to-PDF conversion. Each method has its own strengths, weaknesses, and ideal use cases. We'll provide interactive examples using CustomJS so you can test each approach and see the results in real-time.



Approach 1: CSS @page with counter()

The most elegant and standards-compliant approach is using CSS Paged Media with @page rules and the counter(page) function. This CSS feature was specifically designed for print layouts and allows you to place content in page margin boxes like @bottom-center, @bottom-right, etc.

How It Works

CSS defines special page margin boxes where you can place running content that appears on every page. You can access the current page number with counter(page) and the total page count with counter(pages).

HTML Input

PDF Output

The generated PDF will be displayed here.


Pros

  • Standards-compliant: Part of the CSS Paged Media specification
  • Clean separation: Footer logic stays in CSS, not cluttering HTML
  • Automatic: No JavaScript needed, works purely with CSS
  • Flexible positioning: Multiple margin boxes available (top-left, bottom-center, etc.)

Cons

  • Limited browser support: Chrome/Chromium don't support counter(pages) for total page count
  • Requires specialized PDF engines: Works well with PrinceXML, WeasyPrint, but not Chrome headless
  • Limited styling: Page margin boxes have restricted styling capabilities

Browser/Engine Support

  • โœ… PrinceXML: Full support
  • โœ… WeasyPrint: Good support
  • โš ๏ธ Chrome Headless: Partial support (counter(page) works, counter(pages) doesn't)
  • โŒ Standard browsers: Very limited support

Best Use Cases

Ideal when you're using PDF engines like PrinceXML or WeasyPrint and need a clean, CSS-only solution. Not recommended for Chrome-based solutions unless you only need current page numbers (not total pages).


Approach 2: Fixed Position Footer (Manual)

A simple but effective approach is to use CSS position: fixed with bottom: 0 to create a footer that appears on every printed page. This works across all browsers and PDF engines, making it the most compatible solution.

How It Works

By setting a footer element to position: fixed within a @media print block, the footer automatically repeats on every page during PDF generation. You add manual page numbers or use JavaScript to inject them.

HTML Input

PDF Output

The generated PDF will be displayed here.


Pros

  • Universal support: Works in all browsers and PDF engines
  • Simple implementation: Just CSS, no complex logic needed
  • Full styling control: You can style the footer however you want
  • Works with Chrome Headless: Perfect for CustomJS and Puppeteer

Cons

  • No automatic page numbers: You need to add them manually or via JavaScript
  • Content overlap: You must manually add bottom padding/margin to prevent content from hiding under the footer
  • Static content only: Can't show dynamic "Page X of Y" without JavaScript

Browser/Engine Support

  • โœ… All PDF engines: Universal support
  • โœ… Chrome Headless/CustomJS: Works perfectly
  • โœ… Standard browsers: Full support

Best Use Cases

Perfect for simple footers with static content (company name, document title, date). Also great when combined with JavaScript for dynamic page numbers. This is the most reliable approach for Chrome-based PDF generation.


Approach 3: JavaScript-Generated Page Numbers

This approach combines fixed-position footers with JavaScript to dynamically inject page numbers. Before generating the PDF, JavaScript calculates the total number of pages and inserts "Page X of Y" text into footer elements.

How It Works

The HTML includes placeholder footer elements on each page. JavaScript runs before PDF generation to: 1) Calculate total pages by measuring document height vs page height 2) Insert dynamic page numbers into each footer placeholder 3) Then the PDF is generated with all page numbers in place

HTML Input

PDF Output

The generated PDF will be displayed here.


Pros

  • Full page number support: Shows "Page X of Y" with accurate total count
  • Works everywhere: Compatible with all browsers and PDF engines
  • Highly customizable: You can format page numbers however you want
  • Dynamic content: Can add timestamps, user names, or any calculated data

Cons

  • Requires JavaScript: More complex than pure CSS solutions
  • Timing issues: Must run after page layout is complete but before PDF generation
  • Page calculation complexity: Estimating page count can be tricky with variable content
  • Manual maintenance: Need to ensure JavaScript runs at the right time

Browser/Engine Support

  • โœ… Chrome Headless/CustomJS: Perfect fit, full control over timing
  • โœ… Puppeteer: Excellent support with page.evaluate()
  • โœ… All PDF engines: Works universally once JavaScript has run

Best Use Cases

Ideal for Chrome-based PDF generation where you need accurate "Page X of Y" footers. Perfect when using CustomJS or Puppeteer, as you can execute the JavaScript before PDF generation.


Approach 4: Running Headers/Footers with Named Pages

An advanced CSS Paged Media technique using named pages and running elements. This allows you to define different headers/footers for different sections of your document (e.g., different footer for title page vs content pages).

How It Works

You define named page types using @page chapter, @page cover, etc. Then assign elements to specific pages with page: chapter; in CSS. Each named page can have its own header/footer configuration.

HTML Input

PDF Output

The generated PDF will be displayed here.


Pros

  • Section-specific footers: Different footers for different document sections
  • Professional layouts: Creates book-quality pagination
  • Running elements: Can extract content from document to use in headers
  • Standards-based: Part of CSS Paged Media Level 3 specification

Cons

  • Very limited browser support: Only works with specialized PDF engines
  • Not supported in Chrome: Won't work with Chrome Headless or CustomJS
  • Complex syntax: Steeper learning curve than other approaches
  • Engine-specific quirks: Behavior varies between PrinceXML and WeasyPrint

Browser/Engine Support

  • โœ… PrinceXML: Excellent support with extensions
  • โš ๏ธ WeasyPrint: Partial support, some features missing
  • โŒ Chrome Headless/CustomJS: Not supported
  • โŒ Standard browsers: No support

Best Use Cases

Best for complex documents like books, manuals, or reports with multiple sections that need different pagination styles. Only viable when using PrinceXML or similar professional PDF engines.


Approach 5: Table-Based Pagination

This creative approach uses HTML tables with thead and tfoot elements that automatically repeat on every page. By placing footer content in <tfoot>, browsers will repeat it at the bottom of each printed page.

How It Works

You wrap your entire document content in a table. The <tfoot> element contains your footer, and browsers automatically repeat <thead> and <tfoot> on every page when a table spans multiple pages during printing.

HTML Input

PDF Output

The generated PDF will be displayed here.


Pros

  • Automatic repetition: Browsers handle footer repetition natively
  • No JavaScript needed: Pure HTML/CSS solution
  • Wide browser support: Works in Chrome, Firefox, and most PDF engines
  • Bonus headers: Can also add repeating headers with <thead>

Cons

  • Semantic misuse: Using tables for layout is not semantically correct
  • Styling limitations: Table styling can be restrictive
  • No dynamic page numbers: Can't show "Page X of Y" without JavaScript
  • Layout constraints: Everything must fit within table structure

Browser/Engine Support

  • โœ… Chrome Headless/CustomJS: Works well
  • โœ… Firefox: Good support
  • โš ๏ธ Safari: Works but with some quirks
  • โœ… Most PDF engines: Generally supported

Best Use Cases

Useful for tabular reports or invoices where table structure makes sense. Good fallback option when you need repeating footers without JavaScript but can't use position:fixed or @page rules.


Approach 6: CustomJS Automatic Pagination

CustomJS offers built-in pagination features that automatically handle page numbers and footers for you. This is the most developer-friendly approach, combining the best aspects of all previous methods while handling the complexity behind the scenes.

How It Works

CustomJS provides special configuration options when generating PDFs. You can specify footer templates with variables like and , and CustomJS automatically injects the correct values on every page.

HTML Input

PDF Output

The generated PDF will be displayed here.


Pros

  • Zero configuration: Works out of the box with simple templates
  • Accurate page numbers: Always shows correct "Page X of Y"
  • Professional quality: Consistent, reliable output every time
  • Flexible templates: Support for custom HTML/CSS in headers and footers
  • No timing issues: CustomJS handles all the complexity of when to inject page numbers

Cons

  • Requires CustomJS: Only works with CustomJS service, not a general solution
  • Service dependency: Need an API key and internet connection

Browser/Engine Support

  • โœ… CustomJS: Perfect support (it's built for this!)
  • โŒ Other engines: CustomJS-specific feature

Best Use Cases

Perfect for production applications where you need reliable, professional pagination without dealing with browser quirks or JavaScript complexity. Ideal when you're already using CustomJS for HTML-to-PDF conversion.


Comparison & Recommendations

Here's a comprehensive comparison of all six approaches to help you choose the right one for your use case:

ApproachComplexityPage NumbersChrome SupportBest For
1. CSS @pageLowโœ… Automaticโš ๏ธ PartialPrinceXML, WeasyPrint
2. Fixed PositionLowโŒ Manualโœ… FullStatic footers
3. JavaScriptMediumโœ… Dynamicโœ… FullChrome-based, Puppeteer
4. Named PagesHighโœ… Section-specificโŒ NoneComplex documents, books
5. Table-BasedLowโŒ Manualโœ… GoodReports, invoices
6. CustomJSVery Lowโœ… Automaticโœ… PerfectProduction apps

๐Ÿ“‹ Decision Guide

Using PrinceXML or WeasyPrint?

โ†’ Choose Approach 1 (CSS @page) for the cleanest, most standards-compliant solution.

Need static footers only (no page numbers)?

โ†’ Choose Approach 2 (Fixed Position) for maximum compatibility and simplicity.

Using Chrome Headless or Puppeteer?

โ†’ Choose Approach 3 (JavaScript) for full control with "Page X of Y" support.

Building a production application?

โ†’ Choose Approach 6 (CustomJS) to avoid all the complexity and get professional results immediately.

Creating complex multi-section documents?

โ†’ Choose Approach 4 (Named Pages) if you're committed to PrinceXML and need section-specific pagination.

Working with tabular data?

โ†’ Choose Approach 5 (Table-Based) for invoices and reports where table structure is natural.

๐Ÿ† Our Recommendation

For most modern web applications generating PDFs from HTML, we recommend CustomJS (Approach 6). It provides the best developer experience, most reliable output, and eliminates all the browser compatibility headaches. The built-in pagination features are production-ready and handle edge cases that would take weeks to solve manually.

If you need a free, self-hosted solution, use Approach 3 (JavaScript) with Chrome Headless or Puppeteer. It requires more code but gives you full control and works reliably.

Avoid Approach 4 (Named Pages) unless you're already invested in PrinceXML, as the limited browser support makes it impractical for most use cases.

Related Articles

Continue reading on similar topics

HTML & CSS Tips for Better PDFs
ยทGuide

HTML & CSS Tips for Better PDFs

Master print styles with our Print CSS Cheatsheet. Learn tips, tricks, and code examples to create professional, print-ready HTML documents.

pdfhtml-to-pdfcss
HTML to PDF API: CustomJS vs Alternatives
ยทComparison

HTML to PDF API: CustomJS vs Alternatives

Compare HTML to PDF conversion APIs for price, speed, and JavaScript support. CustomJS offers 600 free PDFs/month and easy Make.com integration.

pdfhtml-to-pdf