Node.js · Handlebars · Puppeteer (Chromium)

pdf-creator-node

Turn HTML templates into PDFs — write templates with Handlebars, get a file, buffer, or stream.

npm i pdf-creator-node

Node 18+ · Puppeteer bundles Chromium (large install). This site documents the public API; the readme has the same detail plus upgrade notes from PhantomJS-era releases.

Quick start

Minimal example: load a template, pass data, call create.

var pdf = require("pdf-creator-node");
var fs = require("fs");

var html = fs.readFileSync("template.html", "utf8");

var options = {
  pdfChrome: {
    layout: { format: "A3", orientation: "portrait", border: "10mm" },
    header: { title: "My report" },
    footer: { copyright: "© 2026", showPageNumbers: true },
  },
};

var document = {
  html: html,
  data: { users: [ /* … */ ] },
  path: "./output.pdf",
  type: "", // "" = file | "buffer" | "stream"
};

pdf
  .create(document, options)
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

API & TypeScript types

The library registers the built-in ifCond helper once at import time. Each create() call uses an isolated Handlebars instance plus any per-call handlebarsHelpers.

Exports

  • Default export{ create } (same as import pdf from "pdf-creator-node" then pdf.create).
  • Namedcreate, buildPdfChrome, htmlPdfTokensToPuppeteer.
  • TypesPdfDocument, PdfDocumentFile, PdfDocumentBuffer, PdfDocumentStream, PdfCreateOptions, PdfRenderOptions, PdfFileInfo, FileInfo (alias of PdfFileInfo), PdfChromeOptions, PdfLayout, PdfHeaderConfig, PdfFooterConfig, IfCondOptions, Document (deprecated; use PdfDocument).
import pdf, { create, buildPdfChrome, htmlPdfTokensToPuppeteer } from "pdf-creator-node";
import type {
  PdfDocument,
  PdfCreateOptions,
  PdfRenderOptions,
  PdfChromeOptions,
  PdfFileInfo,
} from "pdf-creator-node";

// Equivalent:
await pdf.create(doc, opts);
await create(doc, opts);

PdfCreateOptions is PdfRenderOptions plus optional handlebarsHelpers and pdfChrome. Top-level PDF fields override the same keys merged from pdfChrome.

create(document, options)

Compiles document.html with Handlebars and document.data, renders with headless Chromium, and returns a Promise:

  • File (default): path set, type omitted, "", or "file" → resolves to PdfFileInfo ({ filename: string }).
  • Buffer: type: "buffer"Buffer.
  • Stream: type: "stream"NodeJS.ReadableStream.

If html is a fragment (no <!DOCTYPE> / <html>), the renderer wraps it in a minimal HTML document before load.

PDF engine options

Options follow the historical html-pdf shape where possible. v4 maps them to Puppeteer; only the fields below are honored by the renderer unless noted.

Applied by v4 (Chromium)

  • Page size: format (A3, A4, A5, Legal, Letter, Tabloid), orientation, or custom width + height (CSS units, e.g. 8in, 210mm).
  • Margins: border — string or per-side { top, right, bottom, left }.
  • Header / footer: header.contents (HTML string); footer.contents — only one template is used, in order: default, else first, else last. Per-page numeric keys are ignored. Use {{page}} and {{pages}} in HTML; they are converted to Chromium print tokens automatically.
  • Assets: base — base URL for relative src/href (use a trailing slash for a directory).
  • Timing: timeout (ms, default 30000) for navigation and setContent; renderDelay — only a number (ms) is applied after load; the string "manual" has no effect.
  • Network: httpHeaders (extra HTTP headers for the page), httpCookies (passed to page.setCookie before load).
  • Internals: PDFs are generated with printBackground: true so background colors and images print.
// Optional: timing & network (merge with your other options)
{
  timeout: 60000,
  renderDelay: 250,
  httpHeaders: { "Accept-Language": "en-US" },
  httpCookies: [
    { name: "session", value: "abc", path: "/", domain: "example.com" },
  ],
}

In types only / no effect in v4

These exist on PdfRenderOptions for backward compatibility but are not used by the Puppeteer pipeline: directory, paginationOffset, zoomFactor, type (png/jpeg), quality, childProcessOptions, and the deprecated PhantomJS fields phantomPath, phantomArgs, localUrlAccess, script.

Upgrading from PhantomJS / v2

v4 is Chromium-based — layout can differ slightly. Footer contents still uses one slot (default, else first, else last). Phantom-only options are ignored. See the readme — upgrading from v2.

Step-by-step

From install to your first PDF in six steps.

  1. Install
    npm i pdf-creator-node
  2. Load the package and read your template
    var pdf = require("pdf-creator-node");
    var fs = require("fs");
    var html = fs.readFileSync("template.html", "utf8");
  3. Author your HTML

    Use Handlebars (e.g. {{#each users}}).

    <!DOCTYPE html>
    <html>
      <head><meta charset="utf-8" /><title>Hello</title></head>
      <body>
        <h1>User list</h1>
        <ul>
          {{#each users}}
          <li>Name: {{this.name}}</li>
          <li>Age: {{this.age}}</li>
          {{/each}}
        </ul>
      </body>
    </html>
  4. Configure PDF options

    Size with height/width (e.g. 10.5in; units: mm, cm, in, px) or format (A3, A4, A5, Legal, Letter, Tabloid) and orientation. Full field list: PDF engine options.

    var options = {
      format: "A3",
      orientation: "portrait",
      border: "10mm", // page margins
      header: {
        height: "45mm",
        contents: '<div style="text-align: center;">Author: …</div>',
      },
      footer: {
        height: "28mm",
        contents: {
          default: '<span>{{page}}/{{pages}}</span>',
        },
      },
    };

    v4: One footer template is used (default, else first, else last). Per-page keys like 2: are not supported — see readme.

  5. Build the document object

    Set type to "buffer" or "stream" to avoid writing a file.

  6. Call pdf.create

    pdf.create(document, options) returns a Promise.

Layout, header, footer, copyright

v4 uses Puppeteer (Chromium), not PhantomJS. Use pdfChrome on the second argument for paper size, repeating header (title or html), and footer with optional copyright and page numbers ({{page}} / {{pages}}). Plain strings are HTML-escaped. Direct format / header / footer on the same object override pdfChrome.

var options = {
  pdfChrome: {
    layout: {
      format: "A4",
      orientation: "portrait",
      border: "12mm",
    },
    header: { title: "Quarterly report" },
    footer: {
      copyright: "© 2026 My Company",
      showPageNumbers: true,
    },
  },
};

pdf.create(document, options);

pdfChrome.header accepts height, raw html, or a plain title (centered, escaped). pdfChrome.footer accepts height, raw html, copyright, and showPageNumbers. With copyright only, page numbers default on unless showPageNumbers: false. For page numbers only, set showPageNumbers: true and omit copyright.

For low-level control, use the same option object as in PDF engine options (and Puppeteer concepts).

Per-call Handlebars helpers

Pass handlebarsHelpers on the second argument. They are registered only for that render and do not replace the global ifCond registration.

pdf.create(
  {
    html: "<p>{{upper name}}</p>",
    data: { name: "Ada" },
    path: "./out.pdf",
  },
  {
    format: "A4",
    handlebarsHelpers: {
      upper: function (s) { return String(s).toUpperCase(); },
    },
  }
);

Advanced exports

Compose PDF options yourself or convert legacy header/footer tokens for custom pipelines.

buildPdfChrome(pdfChrome?)

Returns a partial PdfRenderOptions object built from PdfChromeOptions (layout, header, footer). Merge it with your own fields the same way the library does: explicit header, footer, format, etc. on the final options object should win.

var partial = buildPdfChrome({
  layout: { format: "A4", border: "10mm" },
  footer: { copyright: "© 2026" },
});
// partial.footer, partial.format, … — merge with your own opts before calling create().

htmlPdfTokensToPuppeteer(html)

Replaces {{page}} and {{pages}} in a string with the span classes Chromium expects for print header/footer templates. The library applies this when building print templates; export is for reuse or testing.

Validation

Invalid input throws before the browser launches. Messages are prefixed with pdf-creator-node:.

  • pdf-creator-node: `document` is required.
  • pdf-creator-node: `document.html` must be a non-empty string.
  • pdf-creator-node: `document.data` is required (use `{}` if the template has no variables).
  • pdf-creator-node: `document.path` is required when writing a PDF file (use `type: "buffer"` or `type: "stream"` for in-memory output).

Handlebars compile/render errors throw pdf-creator-node: template rendering failed: … with the underlying message.

ifCond helper

Compare two values inside templates — two operands only.

Operators: ==, ===, !=, !==, <, <=, >, >=, &&, ||.

{{#ifCond inputData "===" toCheckValue}}
  …
{{/ifCond}}

Fonts & images

Common questions from GitHub issues — v4 uses Chromium, so CSS and web fonts behave like in a browser.

  • Custom fonts: Google Fonts via <link>, or @font-face with local files. For relative file URLs, set base in options to your asset directory (trailing slash). Repeat font setup in header/footer HTML if needed — those run in a separate print context.
  • Margins: use border (same as older versions).
  • Images: remote https://, local paths with base, or data:image/…;base64,…. WebP usually works; if not, try PNG/JPEG. Header/footer images: prefer absolute URLs or paths that resolve with base.
  • Multi-page layout: horizontal scroll in a browser is not scrollable in a PDF — use orientation: "landscape", tighter typography, or table/CSS that fits the page. Try break-inside: avoid / page-break-inside: avoid where Chromium’s print rules allow.

AWS Lambda & serverless

PDF generation uses a full Chromium launch per create() unless you wrap it yourself — plan for install size, memory (~1.5–3 GB), and timeouts.

  • Default puppeteer downloads Chromium (hundreds of MB). Many AWS Lambda zip deployments exceed the unzipped size limit; container images or a trimmed Chromium (e.g. community Lambda layers) are common approaches.
  • The library launches the browser with --no-sandbox, --disable-setuid-sandbox, and --disable-dev-shm-usage (typical for containers). There is no public option for executablePath or extra launch flags — custom binaries may need a fork or wrapper until those options exist.
  • For heavy workloads, dedicated VMs or services (ECS, Fargate, a small PDF microservice) are often simpler than squeezing stock Puppeteer into Lambda.

Author

Maintainer of pdf-creator-node — building simple HTML-to-PDF tools for Node.js. Say hello on the networks below or open an issue on GitHub.

Shyam Hajare

Open-source maintainer · Node.js & PDF tooling

I maintain this library and related experiments around reports, invoices, and server-side document generation. Contributions, bug reports, and feature ideas are welcome on the repository.

hajareshyam@gmail.com

Profiles

If a profile URL is outdated, edit docs/index.html in the repository.