npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pdf-creator-node

v4.0.1

Published

node pdf creator

Downloads

67,701

Readme

pdf-creator-node

Convert HTML + Handlebars templates to PDF in Node.js using Puppeteer (headless Chromium). PhantomJS / html-pdf are no longer used as of v4.

Requirements: Node.js 18 or newer.

Install size: puppeteer downloads a compatible Chromium build on npm install (hundreds of MB). In CI you can cache the browser directory or set PUPPETEER_CACHE_DIR / PUPPETEER_SKIP_DOWNLOAD as needed.

Strengths and trade-offs

| | | | --- | --- | | Strengths | HTML + Handlebars — quick to build invoices, reports, and letters without drawing coordinates. v4+ uses Chromium, so you get modern CSS (flexbox, grid, web fonts) and a maintained rendering stack — not deprecated PhantomJS. pdfChrome helps with layout, headers, footers, and copyright in one place. | | Trade-offs | Footprint: Chromium adds install size and RAM per browser instance. Throughput: launching a browser is heavier than a pure-JS library like PDFKit for tiny one-off jobs; for high volume, reuse browsers (Puppeteer patterns), queue work, or run workers. Model: this package is HTML → PDF; if you need a drawing API (paths, precise vector control) without HTML, use PDFKit / pdf-lib instead. Print pipeline: PDFs use Chrome’s print path — edge cases can differ from on-screen CSS (true for any headless-Chrome PDF approach). |

Practical verdict: strong fit for small and medium projects and production workloads where you control memory and concurrency; for very large scale (always-on millions of PDFs/day), plan infrastructure (pooling, autoscaling, or a dedicated rendering service) like any Chromium-based pipeline.

Upgrading from v2.x (Phantom / html-pdf)

  • v4 is a major release: PDFs are rendered with Chromium, so layout can differ slightly from Phantom.
  • Options are still the same shape for most fields (format, orientation, border, header, footer, pdfChrome).
  • Footer contents: only one template is applied. We use default, else first, else last. Per-page keys (e.g. page 2) are not supported in v4.
  • phantomPath, phantomArgs, etc. are ignored (see PdfRenderOptions in types).
  • {{page}} / {{pages}} in header/footer HTML are converted to Puppeteer’s print tokens automatically.

Install

npm i pdf-creator-node

Quick usage

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

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

const options = {
  format: "A3",
  orientation: "portrait",
  border: "10mm",
};

const document = {
  html,
  data: { users: [{ name: "Ada", age: 36 }] },
  path: "./output.pdf",
  // type: ""  // optional; omit or use "" / "file" for file output
};

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

TypeScript

import pdf from "pdf-creator-node";
import type { PdfDocument, PdfCreateOptions } from "pdf-creator-node";

const document: PdfDocument = {
  html: "<p>{{title}}</p>",
  data: { title: "Hello" },
  path: "./out.pdf",
};

const options: PdfCreateOptions = {
  format: "A4",
  orientation: "portrait",
};

await pdf.create(document, options);

Output type

  • File (default): set path. You can omit type, or use type: "" or type: "file".
  • Buffer: type: "buffer".
  • Stream: type: "stream".

Layout, header, footer, and copyright (pdfChrome)

Use pdfChrome on the second argument for common paper layout and repeating header/footer. Plain title / copyright strings are HTML-escaped.

  • layout: format, orientation, width, height, border (mapped to Puppeteer’s PDF / margin options).
  • header: html (raw HTML per page) or title (centered text). Default height 45mm when content is set.
  • footer: html or combine copyright with optional showPageNumbers ({{page}} / {{pages}}). With copyright only, page numbers default on unless you set showPageNumbers: false. For page numbers only, set showPageNumbers: true and omit copyright. Default footer height 28mm when content is set.

Anything you set directly on the options object (format, header, footer, …) overrides the matching field from pdfChrome.

pdf.create(document, {
  pdfChrome: {
    layout: { format: "A4", orientation: "portrait", border: "12mm" },
    header: { title: "Quarterly report" },
    footer: { copyright: "© 2026 My Company", showPageNumbers: true },
  },
});

Advanced: buildPdfChrome(pdfChrome) returns partial PdfRenderOptions if you want to compose manually (also exported).

Optional Handlebars helpers

Pass handlebarsHelpers on the second argument (alongside PDF options). Helpers apply only to that render (isolated Handlebars instance). Built-in ifCond is always registered.

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

Validation errors

You may see explicit errors such as:

  • document.html must be a non-empty string
  • document.data is required (use {} if there are no variables)
  • document.path is required when writing a file (or use buffer / stream)
  • template rendering failed: … (Handlebars compile/render issue)

HTML template

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

PDF options (Puppeteer)

Examples:

  • Size: "height": "10.5in", "width": "8in" (CSS units), or "format": "Letter" (A3, A4, A5, Legal, Letter, Tabloid) with "orientation".
  • border: margin around the page content (string or per-side object).
  • header / footer: contents is HTML. In the footer, prefer contents.default for the repeating footer; {{page}} and {{pages}} work as with older versions (mapped for Chromium print).
const options = {
  format: "A3",
  orientation: "portrait",
  border: "10mm",
  header: {
    height: "45mm",
    contents: '<div style="text-align: center;">Author: Shyam Hajare</div>',
  },
  footer: {
    height: "28mm",
    contents: {
      default:
        '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>',
    },
  },
};

Margins

Page margins are the border option (string or per-side object). That maps to Chromium’s PDF margins—the content area is inset by this amount.

Custom fonts

v4 renders with Chromium, so fonts work like in a normal web page: @font-face, font-family, and hosted fonts.

Hosted fonts (Google Fonts, jsDelivr, etc.) — add a <link> in <head> and set font-family in your CSS. The process must be able to reach the font URL.

Local files — use @font-face with a relative url('fonts/MyFont.woff2') and set base on the options object to the folder that contains those paths (it becomes Puppeteer’s baseURL). Use a trailing slash when base is a directory:

const path = require("path");

const options = {
  format: "A4",
  base: path.join(__dirname, "assets") + path.sep,
};

Header / footer HTML is rendered in Chromium’s print header/footer UI. It does not automatically inherit styles from the main template. To use the same font there, repeat a <link> / @font-face in the header.contents / footer.contents string, or rely on system fonts for those snippets.

Images

Remote URLs, local files, base64 data URLs, and WebP:

  • Remote: src="https://..." works when the environment can fetch the URL.
  • Local files: set base and use a path relative to that root (same as for fonts).
  • Data URLs: data:image/png;base64,... (use the correct MIME type). WebP is generally supported in Chromium; if something fails, try PNG or JPEG.
  • Images inside header / footer: prefer absolute https:// URLs, or paths that resolve correctly with your base; relative paths are easy to get wrong in the print template.

Wide tables or “horizontal scroll” in a browser do not carry over as scroll in a PDF—use orientation: "landscape", smaller type, or table/CSS layout that fits the page width. For keeping blocks together on one page, try CSS break-inside: avoid / page-break-inside: avoid (behavior follows Chromium’s print rules).

AWS Lambda and serverless

It can work, but not with a default “zip only” deploy in most cases. This package runs Puppeteer with headless Chromium, which is large and memory-hungry.

| Topic | Guidance | | --- | --- | | Deployment size | Default puppeteer installs a full Chromium build (hundreds of MB). AWS Lambda zip deployments have a 250 MB unzipped limit, so the stock layout often does not fit. | | What usually works | Lambda container images (Docker) with Chromium + Node, or a trimmed Chromium packaged for Lambda (community layers / @sparticuz/chromium–style setups) with puppeteer-core. | | This library today | puppeteer.launch() is called with --no-sandbox, --disable-setuid-sandbox, and --disable-dev-shm-usage (typical for containers and many serverless images). There is no public option to set executablePath or merge extra launch flags—using a custom Chromium binary may require a fork, patch, or a wrapper that replaces how the browser is launched until such options exist. | | Memory & timeout | Plan for ~1.5–3 GB RAM and a timeout that covers cold start + browser launch + PDF generation. | | Alternatives | Run PDF generation on ECS/Fargate, EC2, or a dedicated microservice; keep Lambda for orchestration only. |

For CI and non-Lambda servers, you can tune install size with PUPPETEER_SKIP_DOWNLOAD / PUPPETEER_CACHE_DIR when you supply your own browser.

ifCond helper

Compare two values in the template:

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

Example with each:

<ul>
  {{#each users}}
  <li>
    {{this.name}}
    {{#ifCond this.age "===" "26"}}
      (twenty-six)
    {{/ifCond}}
  </li>
  {{/each}}
</ul>

Supported operators: ==, ===, !=, !==, <, <=, >, >=, &&, ||.

Note: Only two operands are compared per ifCond.

License

MIT