Best Practices

Tips for creating production-ready PDF templates

Follow these best practices to create reliable, professional PDF templates that perform well at scale.

PDFs are print documents, not web pages. Use print-oriented CSS to ensure your templates render correctly:

Set the Page Size and Margins

Use the @page rule to define the paper size and margins:

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

Common page sizes:

SizeDimensions
A4210mm x 297mm (standard in Europe)
Letter216mm x 279mm (standard in North America)
A3297mm x 420mm
Legal216mm x 356mm

You can also specify custom dimensions:

@page {
  size: 200mm 300mm;
}

Use Print Media Queries

Wrap print-specific styles in a @media print block to ensure they only apply during PDF generation:

@media print {
  body {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  .no-print {
    display: none;
  }
}

The print-color-adjust: exact property ensures background colors and images are rendered in the PDF. Without it, browsers may strip backgrounds to save ink.

Controlling Page Breaks

Page breaks are critical for multi-page documents. Use these CSS properties to control where pages split:

Prevent Breaking Inside an Element

Keep related content together on the same page:

.card,
.table-row-group,
.section {
  page-break-inside: avoid;
}

Force a Page Break Before or After

Start a new page before or after an element:

.chapter-title {
  page-break-before: always;
}

.section-end {
  page-break-after: always;
}

Practical Example: Invoice with Page Break Control

/* Keep each line item row from splitting across pages */
.line-item {
  page-break-inside: avoid;
}

/* Keep the totals section with the last few items */
.totals-section {
  page-break-inside: avoid;
}

/* Start terms and conditions on a new page */
.terms {
  page-break-before: always;
}

Avoid Orphaned Headings

Prevent headings from appearing alone at the bottom of a page:

h1, h2, h3, h4 {
  page-break-after: avoid;
}

Loading Fonts

System Fonts

The rendering engine includes common system fonts. For maximum compatibility, use a well-defined font stack:

body {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

Google Fonts

Load Google Fonts using @import at the beginning of your CSS:

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

body {
  font-family: 'Inter', sans-serif;
}

Custom Fonts via Base64

For guaranteed font availability, embed fonts directly as base64 in your CSS:

@font-face {
  font-family: 'CustomFont';
  src: url(data:font/woff2;base64,d09GMgABAAAA...) format('woff2');
  font-weight: 400;
  font-style: normal;
}

body {
  font-family: 'CustomFont', sans-serif;
}

This approach adds to the payload size but eliminates external dependencies and ensures consistent rendering.

Performance Optimization

Keep Payload Size Small

The size of your API request directly affects generation time. Follow these guidelines:

  • Minimize HTML -- Remove unnecessary wrapper elements and whitespace.
  • Compress CSS -- Remove unused styles. Only include what the template needs.
  • Optimize images -- Compress images before encoding them as base64. Use JPEG for photos and PNG for graphics with transparency.
  • Avoid excessive base64 fonts -- Load only the font weights you actually use.

Optimize Image Handling

<!-- Recommended: Use compressed, appropriately sized images -->
<img src="data:image/jpeg;base64,/9j/4AAQ..." width="200" height="80" />

<!-- Avoid: Large uncompressed images -->
<img src="data:image/png;base64,iVBORw0KGgo..." />

If referencing external image URLs, ensure they are served from a fast CDN and are publicly accessible:

<img src="https://cdn.example.com/logo-200x80.png" width="200" height="80" />

Pre-Format Data

Format all dates, currencies, and numbers in your application before sending them to DoCreate. This keeps your templates simple and avoids the need for complex formatting logic:

{
  "data": {
    "invoiceDate": "17.02.2026",
    "total": "7.187,60 EUR",
    "taxRate": "19%"
  }
}

Template Design Tips

Use a Consistent Grid

Define a layout grid to keep your documents aligned and professional:

.container {
  max-width: 100%;
  padding: 0;
}

.row {
  display: flex;
  gap: 20px;
  margin-bottom: 16px;
}

.col-half {
  flex: 1;
}

.col-third {
  flex: 0 0 calc(33.333% - 14px);
}

Design for the Right Viewport

A4 paper at 96 DPI is approximately 794px wide. Design your templates to fit within this width:

body {
  width: 100%;
  max-width: 794px;
  margin: 0 auto;
}

Use Border Instead of Background for Table Rows

For large tables, borders render more reliably than alternating background colors across page breaks:

/* More reliable across page breaks */
.line-item {
  border-bottom: 1px solid #eee;
}

/* Can cause visual artifacts at page breaks */
.line-item:nth-child(even) {
  background-color: #f9f9f9;
}

Test with Realistic Data

Always test your templates with:

  • Long text values -- Names, addresses, and descriptions that might wrap to multiple lines.
  • Many line items -- Test with enough items to trigger multiple pages.
  • Empty or missing fields -- Verify that conditional sections handle absent data gracefully.
  • Special characters -- Umlauts, accents, and symbols (e.g., EUR, GBP).

Checklist for Production Templates

Before deploying a template to production, verify the following:

  • [ ] Page size and margins are set via @page
  • [ ] print-color-adjust: exact is enabled for background colors
  • [ ] Page breaks are controlled with page-break-inside: avoid on key sections
  • [ ] Headings use page-break-after: avoid to prevent orphaned titles
  • [ ] Fonts load correctly (test with the actual PDF output)
  • [ ] Images are compressed and sized appropriately
  • [ ] The template handles long content and multi-page scenarios
  • [ ] Conditional sections work with missing data
  • [ ] Headers and footers use inline styles (not the css field)
  • [ ] Page numbers render correctly on multi-page documents

Next Steps