GUIDE

PDF Generation Best Practices for Developers

Layout, performance, accessibility, and architecture patterns for generating PDFs from HTML in production applications.

2026-04-04  ·  10 min read

Generating PDFs in web applications is deceptively complex. What looks straightforward in development — convert this HTML to a PDF — becomes a source of subtle layout bugs, font rendering issues, and performance problems in production. This guide covers the practical patterns that experienced teams use to ship reliable, well-formatted PDF generation at scale.

Choosing Your PDF Generation Approach

Three main approaches dominate production PDF generation in 2026: server-side libraries (mPDF, PDFKit, FPDF), headless browser rendering (Playwright, Puppeteer), and cloud PDF APIs. Each has trade-offs in rendering fidelity, server resource requirements, and maintenance overhead.

ApproachRendering FidelityInfrastructureBest For
Server-side lib (mPDF)Limited CSS supportPHP/Python onlySimple invoices, text-heavy docs
Playwright/PuppeteerPixel-perfect250MB+ browser installComplex layouts, charts
Cloud PDF APIPixel-perfectZero — REST onlyAny language, serverless, scale

HTML Layout Patterns That Render Well in PDFs

PDF rendering differs from browser rendering in one critical way: pages have finite height. Content that flows naturally in a scrollable webpage gets cut at page boundaries in a PDF. Design your HTML templates with page breaks in mind from the start.

/* Force page break before a section */
.new-page {
  page-break-before: always;
  break-before: page;
}

/* Prevent breaking inside an element */
.no-break {
  page-break-inside: avoid;
  break-inside: avoid;
}

/* Table rows — avoid orphaned rows */
tr {
  page-break-inside: avoid;
}

Use print-specific CSS in a media query to override screen-optimised styles. Remove navigation bars, sidebars, and interactive elements. Set explicit page margins using the @page rule to control the printable area precisely.

@page {
  size: A4;
  margin: 20mm 15mm;
}

@media print {
  nav, .sidebar, .cookie-banner { display: none !important; }
  body { font-size: 11pt; color: #000; background: #fff; }
  a { color: inherit; text-decoration: none; }
}

Font Handling in PDF Generation

Font rendering is one of the most common sources of PDF layout bugs. The safe approach is to use web-safe fonts or embed fonts as base64 in the HTML itself. When using Google Fonts or other external font hosts, add a delay parameter to ensure fonts load before the PDF is captured. SnapAPI waits for network idle by default, which covers most Google Fonts scenarios automatically.

/* Self-host fonts for reliability */
@font-face {
  font-family: "Inter";
  src: url("data:font/woff2;base64,AAEAAAA...") format("woff2");
  font-weight: 400;
}

/* Or load from Google Fonts — SnapAPI waits for load */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700');

Headers, Footers, and Page Numbers

The @page rule supports margin boxes that appear on every PDF page. Use them for consistent headers with your logo, footers with page numbers, and document metadata.

@page {
  margin: 25mm 15mm 20mm;
  @top-right {
    content: "Invoice #" attr(data-invoice-id);
    font-size: 9pt;
    color: #666;
  }
  @bottom-center {
    content: "Page " counter(page) " of " counter(pages);
    font-size: 9pt;
    color: #666;
  }
}

Generating PDFs with SnapAPI

SnapAPI's PDF endpoint accepts either a URL to render or a raw HTML string. The Chromium-based renderer supports full CSS3, web fonts, SVG graphics, and JavaScript execution — the same rendering fidelity as Playwright with no infrastructure to manage.

const axios = require('axios');

async function generateInvoicePDF(invoiceHTML) {
  const response = await axios.post(
    'https://api.snapapi.pics/v1/pdf',
    {
      html:        invoiceHTML,
      format:      'A4',
      print_bg:    true,
      margin_top:  '20mm',
      margin_bottom: '20mm',
      margin_left:   '15mm',
      margin_right:  '15mm',
    },
    { headers: { 'X-Api-Key': process.env.SNAP_API_KEY } }
  );
  return response.data.cdn_url; // persistent CDN link
}

Performance and Caching

PDF generation is inherently slower than serving a pre-generated file. Browser rendering, font loading, and PDF encoding typically take 1-3 seconds per document. For customer-facing PDF delivery, generate the PDF when the document is created and store the CDN URL, rather than generating on each download request. This keeps PDF generation off the critical path and ensures consistent delivery performance.

SnapAPI stores generated PDFs on CDN with long-lived URLs. The returned cdn_url is stable and can be stored in your database, emailed directly to customers, or embedded in your application as a permanent download link.

Testing PDF Output

Automate PDF quality checks in your CI pipeline. Use pdf-parse or pdfjs-dist in Node.js to extract text from generated PDFs and assert that required fields are present. For layout regression testing, render the PDF pages as images with pdf2pic and compare against reference screenshots using an image diffing library.

Start Generating PDFs with SnapAPI

200 free PDF generations per month. Full CSS3, web fonts, and SVG support. No Chromium install.

Create Free Account

PDF Accessibility: Making Documents Screen-Reader Friendly

Accessible PDFs require tagged structure so screen readers can traverse the document hierarchy. Chromium-based PDF generation produces tagged PDFs automatically when the HTML uses semantic markup: proper heading levels, alt text on images, label elements on form fields, and lists marked up as ul or ol rather than styled divs.

After generation, validate accessibility with Adobe Acrobat's built-in accessibility checker or the free PAC 2024 tool. For regulated industries — healthcare, finance, government — accessibility compliance may be a legal requirement, not just a best practice.

Adding Watermarks and Digital Signatures

Watermarks can be added at the HTML level before sending to the PDF API. Position a fixed-position div with low opacity across the entire document body. This approach is simpler and more reliable than post-processing the PDF binary with a library.

/* Watermark via HTML CSS */
.watermark {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-45deg);
  font-size: 80px;
  color: rgba(0, 0, 0, 0.08);
  z-index: 9999;
  pointer-events: none;
  white-space: nowrap;
}

Digital signatures require post-processing the generated PDF binary with a library such as node-signpdf or iText. Generate the PDF via SnapAPI, download the binary, apply the signature certificate, and store or deliver the signed document. The PDF CDN URL from SnapAPI returns the binary directly when accessed without a redirect.

Multi-Language and RTL Support

Chromium handles Unicode, bidirectional text, and RTL languages like Arabic and Hebrew natively. Set the lang attribute on the html element and the dir attribute on the body for RTL documents. Use a font that includes the required character ranges — Noto fonts from Google cover virtually every writing system and are freely embeddable in PDF documents.

For CJK characters — Chinese, Japanese, Korean — ensure the font includes the appropriate glyph ranges. Noto Sans CJK or Source Han Sans cover all three writing systems and render correctly in SnapAPI's Chromium renderer without any additional configuration.

Invoice and Receipt Generation

Invoices are one of the most common PDF generation use cases in SaaS applications. A well-structured invoice template needs consistent table alignment, correct number formatting for currencies, and a professional visual layout that reflects your brand. Using HTML and CSS for this gives you far more control than any PDF library's proprietary table API.

Keep invoice templates in version-controlled HTML files rather than building them programmatically with string concatenation. Use a templating engine (Handlebars, Jinja2, Blade, Twig) to inject order data into the template, then pass the rendered HTML to SnapAPI. This keeps template design and data logic cleanly separated.

Report Generation at Scale

Analytics dashboards and data reports rendered as PDFs often include charts, tables, and conditional formatting. Render Chart.js, Recharts, or D3 visualisations in the HTML template. SnapAPI's Chromium browser executes JavaScript before capture, so canvas-based charts render fully in the output PDF.

For weekly or monthly report generation, automate the pipeline: query your database for the reporting period data, inject it into an HTML report template, call the PDF API, and email the result to recipients via SendGrid or Postmark. The entire pipeline — data fetch, HTML render, PDF generation, and email delivery — typically completes in under 10 seconds for a 5-10 page report.

Page Size and Orientation

SnapAPI supports standard paper sizes: A3, A4, A5, Letter, Legal, and Tabloid. For landscape orientation, set the landscape parameter to true. Wide data tables and charts with many columns render better in landscape — switch based on the content type rather than using a single orientation for all documents.

Custom page sizes are also supported for non-standard document formats — long-form scrolls, custom card sizes, or regulatory filing requirements that specify exact dimensions in millimetres. Pass width and height in millimetres via the page_width and page_height parameters to override the format preset entirely.

Key Takeaways

The most important decisions in a PDF generation system are the rendering engine and the caching strategy. Choosing a Chromium-based renderer ensures consistent, pixel-perfect output across all CSS3 features. Pre-generating PDFs at creation time and storing CDN URLs ensures fast delivery without generation latency on the critical path.

Design templates with page breaks in mind from the start, use semantic HTML for accessibility, embed or reliably load fonts, and test PDF output in CI. SnapAPI removes the infrastructure complexity from all of this, providing a managed Chromium PDF renderer accessible from any language via a simple REST call.