GuidesApril 5, 2026

HTML to PDF Best Practices: Page Breaks, Fonts, and Print CSS

A practical guide to producing polished, professional PDF output from HTML — controlling page layout, embedding fonts reliably, managing dynamic content, and setting up headers and footers that work every time.

Use a Dedicated Print Stylesheet

The single biggest improvement you can make to HTML-to-PDF output is a dedicated print stylesheet using the @media print block. This lets you maintain one HTML template that renders correctly in the browser and produces clean PDF output without navigation, sidebars, or interactive elements.

@media print {
  /* Hide browser UI elements */
  nav, .sidebar, .chat-widget, .cookie-banner,
  .no-print, footer { display: none !important; }

  /* Ensure full-width content */
  .container { max-width: 100%; padding: 0; }

  /* Control text size for print */
  body { font-size: 11pt; line-height: 1.5; color: #000; }

  /* Preserve links but show URL */
  a[href]:after { content: " (" attr(href) ")"; font-size: 9pt; opacity: 0.6; }
  a[href^="#"]:after { content: ""; } /* Skip internal links */
}

Set body font size in pt rather than px for print output. 11pt maps to approximately 15px at 96dpi, which is readable at standard zoom levels. Use color: #000 to ensure text is fully black rather than near-black grays that can appear washed out in PDF output.

Controlling Page Breaks

Uncontrolled page breaks are the most common complaint about HTML-to-PDF output. Tables split mid-row, headings appear at the bottom of a page separated from their content, and code blocks are cut in half. Fix these with CSS page break properties:

@media print {
  /* Force a new page before each major section */
  h1, .page-break-before { page-break-before: always; }

  /* Never break inside these elements */
  tr, pre, code, blockquote,
  .invoice-line, .card { page-break-inside: avoid; }

  /* Keep headings with the content that follows */
  h2, h3, h4 {
    page-break-after: avoid;
    break-after: avoid;
  }

  /* For modern browsers — use break-* properties */
  table { break-inside: avoid; }
  thead { display: table-header-group; } /* Repeat table headers */
}

Use both page-break-* and break-* properties for maximum compatibility. break-inside: avoid is the modern standard but page-break-inside: avoid is supported by more rendering engines. Chromium respects both.

The display: table-header-group trick on thead causes the table header row to repeat at the top of every page when a table spans multiple pages — essential for data tables in reports and invoices.

Font Embedding and Typography for PDF

Fonts in PDFs work differently from web fonts. For best results with SnapAPI's Chromium-based PDF engine, use Google Fonts via the standard link tag — Chromium loads and embeds them correctly. Self-hosted fonts also work if they are accessible from your server at the time of capture.

<!-- In your HTML head -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap">

<style>
  body { font-family: 'Inter', -apple-system, sans-serif; }

  @media print {
    /* Force font rendering in PDF */
    * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  }
</style>

The -webkit-print-color-adjust: exact and print-color-adjust: exact properties tell Chromium to preserve background colors and images in print mode. Without these, Chromium strips background colors to save printer ink — resulting in PDFs that look completely different from the browser view.

Avoid System Fonts in PDFs

Do not rely on system fonts like -apple-system or BlinkMacSystemFont as your primary font in PDF templates. These resolve to different typefaces depending on the operating system Chromium runs on. Always specify an explicit web font as the first entry in your font stack, with a generic family as fallback. This ensures consistent typography across all PDF captures regardless of server OS.

Headers, Footers, and Page Numbers

SnapAPI's PDF endpoint accepts header_template and footer_template parameters that inject HTML into the margin area of every page. These templates run in an isolated context with no access to the main document's styles, so you must set all CSS properties explicitly:

{
  "url": "https://app.example.com/report/q1-2026",
  "page_size": "A4",
  "margin_top": "20mm",
  "margin_bottom": "20mm",
  "header_template": "<div style='font-family:Arial;font-size:9pt;color:#666;padding:0 10mm;width:100%;display:flex;justify-content:space-between'><span>Q1 2026 Report</span><span>Confidential</span></div>",
  "footer_template": "<div style='font-family:Arial;font-size:9pt;color:#666;text-align:center;width:100%'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>",
  "print_background": true
}

Set margin_top and margin_bottom large enough to accommodate your header and footer height. A 20mm margin gives enough room for a single line of text with comfortable padding. If the header overlaps the main content, increase the margin value.

Handling Dynamic Content and JavaScript

If your PDF template loads data via JavaScript — charts rendered with D3.js, tables populated via API calls, or dashboards built with React — use the wait_for parameter to delay capture until the content is fully rendered:

{
  "url": "https://app.example.com/report?token=abc123",
  "wait_for": ".chart-rendered",
  "delay": 1000,
  "page_size": "A4",
  "print_background": true
}

Add a chart-rendered class to your chart container after the render completes. The API waits until it sees that class in the DOM before triggering PDF generation. The delay parameter adds an additional millisecond buffer after the selector appears — useful for animations that need to complete before capture.

Generate professional PDFs with SnapAPI at snapapi.pics — 200 free captures per month, no credit card required. The PDF endpoint supports all parameters discussed here and is available on every plan including the free tier.

Designing PDF-Friendly HTML Templates

The most maintainable approach to PDF generation is building templates that look correct in the browser first and then applying print CSS to refine the PDF output. Start with a simple HTML page using a fixed-width container that matches your target paper size in pixels. A4 paper at 96 DPI is approximately 794 by 1123 pixels. Use this as your container width during browser development to see how content will paginate:

/* Development preview styles */
.page {
  width: 794px;
  min-height: 1123px;
  margin: 0 auto;
  padding: 40px;
  box-shadow: 0 0 10px rgba(0,0,0,0.1); /* Visual page boundary */
  background: white;
}

/* For the actual PDF — remove the visual boundary */
@media print {
  .page { width: 100%; box-shadow: none; margin: 0; }
}

This workflow lets you design in the browser at the exact dimensions of the final PDF, catching layout issues early. Once the design looks correct in the browser, it typically requires minimal adjustment for PDF output.

Tables in PDF: Common Problems and Fixes

Tables are a frequent source of PDF layout problems. Common issues include columns that overflow the page width, rows that split across page breaks, and header rows that do not repeat. Apply these CSS rules to address all three:

table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed; /* Prevents overflow */
}

thead { display: table-header-group; } /* Repeat on each page */
tfoot { display: table-footer-group; }

td, th {
  word-wrap: break-word; /* Wrap long text */
  overflow-wrap: break-word;
  padding: 8px 12px;
  border: 1px solid #ddd;
}

tr { page-break-inside: avoid; } /* Keep rows on one page */

table-layout: fixed divides column widths evenly based on the table width rather than content. This prevents wide cells from overflowing the page. Set explicit widths on col elements or th cells to control the column proportions.

Testing and Validating PDF Output

PDF output should be tested across a range of content sizes — short single-page documents, multi-page reports, and edge cases like empty tables or unusually long strings. Build a test suite that generates PDFs from fixture HTML files and checks the page count, file size, and visual output against saved reference PDFs.

For visual regression testing of PDF output, convert PDF pages to PNG images using a library like pdf2image in Python or pdfjs-dist in Node.js, then run pixel diffs against reference images. This catches font rendering changes, layout shifts, and background color regressions that text-based PDF validation misses entirely.

Check the generated PDF file size as a sanity check — a typical one-page A4 invoice with a logo and a table should be between 50 and 200 kilobytes. A PDF that is abnormally large (over 2 MB) typically indicates unoptimized embedded images. Compress images before embedding them in the HTML template, or use CSS background-image with WebP format for logos and decorative graphics.

Apply these best practices with SnapAPI at snapapi.pics — 200 free PDF captures per month, no credit card required. The PDF endpoint is fully documented at snapapi.pics/docs.html with all parameters, response formats, and working code samples for Node.js, Python, Ruby, Go, and C#.

html pdf best practices page break font print css guide
html to pdf print css best practices page break font embedding