HTML to PDF in Node.js: Production Guide

Updated October 30, 2026 · 9 min read

Generating a PDF from HTML in Node.js is easy until production: fonts fail to load, images arrive late, tables split, and long jobs time out. This guide covers the production path with the Fork PDF SDK.

1. Install the SDK

Fork PDF requires Node.js 18 or later. Install the SDK with npm:

npm install forkpdf

Create a client with your API key and project ID. Keep both in environment variables, not in source control.

const { ForkPDFClient } = require('forkpdf');

const client = new ForkPDFClient(
  process.env.FORKPDF_API_KEY,
  process.env.FORKPDF_PROJECT_ID
);

2. Generate your first PDF

generate() accepts HTML, a URL, or a saved template. This example renders inline HTML and returns a Buffer.

const fs = require('node:fs');

const result = await client.generate({
  htmlContent: '<h1>Invoice</h1><p>Hello from Node.js</p>',
  format: 'A4',
  downloadBuffer: true
});

fs.writeFileSync('invoice.pdf', result.pdfBuffer);

Use format or both width and height. The SDK converts custom dimensions to a CSS @page rule.

3. Use templates for repeated documents

Save the layout once in the dashboard and pass data at generation time. Templates are rendered with Nunjucks before PDF generation.

const result = await client.generate({
  templateId: 'YOUR_TEMPLATE_ID',
  data: {
    customer: 'Acme',
    total: '$1,240.00',
    items: [
      { name: 'Design', amount: '$840.00' },
      { name: 'Support', amount: '$400.00' }
    ]
  },
  downloadBuffer: true
});

Keep template assets under the dashboard limit and the expanded payload under the 20 MB request limit. See Limits & file sizes.

4. Handle fonts and images

Use waitUntil: 'networkidle0' when the page depends on remote fonts or images. Prefer image URLs over inline data URIs: a data URI increases both the request HTML and the spatial data.pb, while a URL stays a short attribute.

await client.generate({
  htmlContent,
  format: 'A4',
  waitUntil: 'networkidle0',
  timeout: 30000
});

Embed fonts with @font-face or use system fonts, and wait for them before printing.

5. Prevent broken pages

CSS break-inside: avoid helps for rows and short blocks. For dynamic content, add a testCase that checks dependent elements stay on the same page and returns a patch when they do not.

6. Add page numbers and a TOC

Page numbers are only known after layout. Use a testCase to measure headings, then patch the TOC and re-render. The TOC guide has a complete example.

Table of contents with real page numbers

7. Move long jobs to webhooks

For large documents or high volume, use webhook: true. The API returns immediately and posts the result to your endpoint, with retries and a dead letter queue.

const queued = await client.generate({
  templateId: 'YOUR_TEMPLATE_ID',
  data,
  webhook: true
});

Verify the x-webhook-signature header before trusting the payload. See Webhook delivery.

8. Know the limits

  • API request: up to 20 MB
  • PDF output: up to 25 MB
  • Spatial ZIP: up to 39 MB
  • testResults: up to 8 KB of JSON

Full details: Limits & file sizes.

9. Production checklist

  • Keep the API key and project ID in environment variables.
  • Set a timeout that matches your document size (3–30s).
  • Use networkidle0 for remote assets.
  • Add layout assertions for tables, TOC, signatures, and totals.
  • Use webhooks for long-running or bulk jobs.
  • Log the PDF ID and testResults for support.
  • Pin the SDK version and watch the version headers/changelog.

Generate your first PDF

Start with the API reference, then add a layout check.

Generate PDF API