Node.js · Handlebars · Puppeteer (Chromium)
pdf-creator-node
Turn HTML templates into PDFs — write templates with Handlebars, get a file, buffer, or stream.
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 asimport pdf from "pdf-creator-node"thenpdf.create). -
Named —
create,buildPdfChrome,htmlPdfTokensToPuppeteer. -
Types —
PdfDocument,PdfDocumentFile,PdfDocumentBuffer,PdfDocumentStream,PdfCreateOptions,PdfRenderOptions,PdfFileInfo,FileInfo(alias ofPdfFileInfo),PdfChromeOptions,PdfLayout,PdfHeaderConfig,PdfFooterConfig,IfCondOptions,Document(deprecated; usePdfDocument).
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):
pathset,typeomitted,"", or"file"→ resolves toPdfFileInfo({ 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 customwidth+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, elsefirst, elselast. 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 relativesrc/href(use a trailing slash for a directory). -
Timing:
timeout(ms, default30000) for navigation andsetContent;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 topage.setCookiebefore load). -
Internals: PDFs are generated with
printBackground: trueso 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.
-
Installnpm i pdf-creator-node
-
Load the package and read your template
var pdf = require("pdf-creator-node"); var fs = require("fs"); var html = fs.readFileSync("template.html", "utf8"); -
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> -
Configure PDF options
Size with
height/width(e.g.10.5in; units: mm, cm, in, px) orformat(A3, A4, A5, Legal, Letter, Tabloid) andorientation. 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, elsefirst, elselast). Per-page keys like2:are not supported — see readme. -
Build the document object
Set
typeto"buffer"or"stream"to avoid writing a file. -
Call
pdf.createpdf.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-facewith local files. For relative file URLs, setbasein options to your asset directory (trailing slash). Repeat font setup inheader/footerHTML if needed — those run in a separate print context. -
Margins: use
border(same as older versions). -
Images: remote
https://, local paths withbase, ordata:image/…;base64,…. WebP usually works; if not, try PNG/JPEG. Header/footer images: prefer absolute URLs or paths that resolve withbase. -
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. Trybreak-inside: avoid/page-break-inside: avoidwhere 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
puppeteerdownloads 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 forexecutablePathor 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.