Multi-Page PDF Generation: Headers, Footers, and Page Breaks — TongoRender Blog
Back to Blog
tutorialsmulti-pageheadersfooters

Multi-Page PDF Generation: Headers, Footers, and Page Breaks

Master multi-page PDF generation with CSS page breaks, repeated headers and footers, page numbers, and practical code examples for professional documents.

TongoRender TeamJanuary 30, 202610 min

Single-page PDFs are straightforward, but real-world documents — contracts, reports, manuals, catalogs — span dozens or even hundreds of pages. Managing page breaks, keeping headers and footers consistent, and adding page numbers requires a solid understanding of CSS print styles and PDF API features. This guide covers everything you need to produce professional multi-page PDFs.

CSS Page Break Rules

CSS provides several properties to control where page breaks occur. The modern spec uses break-before, break-after, and break-inside, while the legacy properties page-break-before, page-break-after, and page-break-inside are still widely supported:

Force Page Breaks

/* Start each chapter on a new page */
.chapter { break-before: page; }

/* Alternative legacy syntax (wider browser support) */
.chapter { page-break-before: always; }

/* Force a break after the table of contents */
.toc { break-after: page; }

Prevent Unwanted Breaks

/* Keep headings with their following content */
h2, h3 { break-after: avoid; }

/* Don't break inside cards, figures, or table rows */
.card, figure, tr { break-inside: avoid; }

/* Keep at least 3 lines before/after a break (widows and orphans) */
p { widows: 3; orphans: 3; }

Table-Specific Breaks

Long tables are a common pain point. Here is a pattern that keeps the header visible on every page:

/* The browser will repeat thead on each page */
table { width: 100%; border-collapse: collapse; }
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
tr { break-inside: avoid; }

/* Ensure table does not break in awkward places */
table { break-inside: auto; }

Headers and Footers with TongoRender

TongoRender supports Chromium's built-in header and footer templates. These are rendered separately from the main content and repeated on every page:

const response = await fetch('https://api.tongorender.io/v1/pdf', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.TONGORENDER_API_KEY,
  },
  body: JSON.stringify({
    html: documentHtml,
    format: 'A4',
    margin: { top: '25mm', bottom: '20mm', left: '15mm', right: '15mm' },
    displayHeaderFooter: true,
    headerTemplate: `
      <div style="font-size:9px; color:#666; width:100%; padding:0 15mm; display:flex; justify-content:space-between;">
        <span>Acme Corp — Confidential</span>
        <span>Annual Report 2026</span>
      </div>
    `,
    footerTemplate: `
      <div style="font-size:9px; color:#666; width:100%; padding:0 15mm; display:flex; justify-content:space-between;">
        <span>Generated on 2026-03-15</span>
        <span>Page <span class="pageNumber"></span> of <span class="totalPages"></span></span>
      </div>
    `,
  }),
});

Built-in Template Variables

Chromium provides these CSS classes that are automatically replaced with values:

  • pageNumber — Current page number
  • totalPages — Total number of pages
  • date — Formatted print date
  • title — Document title from <title>
  • url — Document URL

Page Numbers with Custom Styling

For more control over page number styling, use the footer template with CSS:

footerTemplate: `
  <div style="width:100%; text-align:center; font-size:10px; font-family:Arial;">
    <span style="
      background: #1a1a2e;
      color: white;
      padding: 2px 10px;
      border-radius: 10px;
      font-weight: bold;
    ">
      <span class="pageNumber"></span>
    </span>
  </div>
`

Complete Multi-Page Document Example

Here is a full example that combines all techniques — a report with a cover page, table of contents, chapters, and a long data table:

function buildReport(data) {
  return `
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <style>
      body { font-family: 'Georgia', serif; color: #333; line-height: 1.6; margin: 0; padding: 30px; }

      /* Cover page */
      .cover { height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; }
      .cover h1 { font-size: 36pt; color: #1a1a2e; margin-bottom: 10px; }
      .cover .subtitle { font-size: 16pt; color: #666; }
      .cover { break-after: page; }

      /* Table of contents */
      .toc { break-after: page; }
      .toc-item { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px dotted #ccc; }

      /* Chapters */
      .chapter { break-before: page; }
      h2 { color: #1a1a2e; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px; break-after: avoid; }
      h3 { color: #2d3748; break-after: avoid; }

      /* Tables */
      table { width: 100%; border-collapse: collapse; margin: 15px 0; }
      thead { display: table-header-group; background: #1a1a2e; color: white; }
      th, td { padding: 8px 12px; text-align: left; border-bottom: 1px solid #e2e8f0; }
      tr { break-inside: avoid; }
      tr:nth-child(even) { background: #f7fafc; }

      /* Prevent orphans and widows */
      p { orphans: 3; widows: 3; }

      @media print {
        .no-print { display: none; }
      }
    </style>
  </head>
  <body>
    <div class="cover">
      <h1>${data.title}</h1>
      <div class="subtitle">${data.subtitle}</div>
      <div style="margin-top:30px;color:#999">${data.date}</div>
    </div>

    <div class="toc">
      <h2>Table of Contents</h2>
      ${data.chapters.map((ch, i) => \`
        <div class="toc-item"><span>${i + 1}. ${ch.title}</span></div>
      \`).join('')}
    </div>

    ${data.chapters.map(ch => \`
      <div class="chapter">
        <h2>${ch.title}</h2>
        ${ch.content}
      </div>
    \`).join('')}
  </body>
  </html>`;
}

Common Pitfalls

  • Margins too small for headers/footers — The top and bottom margins must be large enough to contain the header and footer templates. If your header is 15mm tall, set the top margin to at least 20mm.
  • Forgetting display: table-header-group — Without this, table headers do not repeat on subsequent pages.
  • Using position: fixed for headers — Fixed positioning does not work for repeating headers in print. Use the API's header/footer templates instead.
  • Background colors not printing — Set printBackground: true in the API options.
  • Ignoring orphans and widows — A single line of a paragraph stranded at the top or bottom of a page looks unprofessional.

With proper CSS print styles and TongoRender's header/footer support, you can generate professional multi-page documents that rival dedicated desktop publishing tools.

Generate multi-page PDFs with TongoRender — 100 free renders per month, no credit card required.

Share this articleShare on Twitter