html-pdf vs html2pdf.js vs jspdf vs pdfmake
Client-Side PDF Generation from HTML and Programmatic Content in Web Applications
html-pdfhtml2pdf.jsjspdfpdfmakeSimilar Packages:

Client-Side PDF Generation from HTML and Programmatic Content in Web Applications

html-pdf, html2pdf.js, jsPDF, and pdfmake are JavaScript libraries used to generate PDF documents in web applications, but they differ significantly in architecture, capabilities, and runtime environments. html-pdf is a Node.js-only package that converts HTML to PDF using PhantomJS and has been deprecated. html2pdf.js runs in the browser and captures DOM elements as PDFs using a combination of html2canvas and jsPDF. jsPDF is a low-level PDF creation library that works in both browser and Node.js environments, allowing programmatic drawing of text, images, and shapes without relying on HTML. pdfmake offers a declarative, document-definition-based approach to PDF generation with strong support for layouts, tables, and styling, and runs in both browser and Node.js contexts.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
html-pdf03,612-4685 years agoMIT
html2pdf.js04,83610.9 MB4933 months agoMIT
jspdf031,21930.2 MB112a month agoMIT
pdfmake012,25815.3 MB235a month agoMIT

Comparing PDF Generation Libraries: html-pdf, html2pdf.js, jsPDF, and pdfmake

Generating PDFs in web applications is a common requirement — whether for invoices, reports, or printable summaries. But the right tool depends heavily on your input source (HTML vs. structured data), runtime (browser vs. Node.js), and quality expectations. Let’s examine how these four libraries handle real-world scenarios.

⚠️ html-pdf: Deprecated and Node-Only

html-pdf was a popular Node.js package that used PhantomJS to render HTML into PDFs. It is now officially deprecated — both on npm and GitHub — and PhantomJS itself is no longer maintained. This means security vulnerabilities won’t be patched, and modern CSS (Flexbox, Grid, etc.) won’t render correctly.

It only works in Node.js, cannot run in the browser, and requires a headless browser environment. For these reasons, do not use html-pdf in new projects.

// html-pdf: Deprecated usage (Node.js only)
const pdf = require('html-pdf');
const html = '<h1>Hello World</h1>';
pdf.create(html).toFile('./output.pdf', (err, res) => {
  if (err) return console.log(err);
  console.log(res.filename);
});

💡 Migration path: Use Puppeteer or Playwright in Node.js for modern, reliable HTML-to-PDF conversion.

🖼️ html2pdf.js: Browser-Based HTML Snapshot

html2pdf.js runs entirely in the browser and is designed to convert any DOM element into a PDF. It combines two tools under the hood:

  • html2canvas: Renders HTML elements to a <canvas> (rasterizing text and layout)
  • jsPDF: Converts that canvas into a PDF

This means the output is pixel-based, not vector text. Zooming in may reveal blurry text, and file sizes can be large. However, it’s incredibly easy to use for capturing visual snapshots of UI components.

// html2pdf.js: Convert a DOM element to PDF
import html2pdf from 'html2pdf.js';

const element = document.getElementById('invoice');
html2pdf().from(element).save();

You can add options for margins, filename, or page size:

html2pdf()
  .set({ margin: 10, filename: 'report.pdf', image: { type: 'jpeg', quality: 0.95 } })
  .from(element)
  .save();

✅ Use when: You need a quick “print this page” button that mirrors the browser-rendered look.
❌ Avoid when: You need searchable text, small file size, or precise typography.

✍️ jsPDF: Low-Level PDF Builder

jsPDF gives you a programmatic API to draw PDFs from scratch. It doesn’t understand HTML or CSS — you position text, images, and shapes using coordinates. This gives you full control but requires manual layout logic.

It works in both browser and Node.js (with canvas polyfills or image support via plugins) and generates vector-based, text-searchable PDFs.

// jsPDF: Create a PDF from code
import { jsPDF } from 'jspdf';

const doc = new jsPDF();
doc.setFontSize(22);
doc.text('Hello world!', 20, 20);
doc.save('output.pdf');

Adding an image requires preloading it:

const img = new Image();
img.src = '/logo.png';
img.onload = () => {
  doc.addImage(img, 'PNG', 15, 40, 180, 160);
  doc.save('with-image.pdf');
};

For tables or page breaks, you’ll need helper libraries like jspdf-autotable. Without them, layout is entirely manual.

✅ Use when: You’re generating simple, data-driven documents (e.g., shipping labels, certificates) and can define layout in code.
❌ Avoid when: You need to replicate complex HTML layouts or want automatic pagination.

📐 pdfmake: Declarative Document Definitions

pdfmake uses a JSON-like document definition to describe PDF structure. You define content as an array of objects (text, tables, images, columns), and pdfmake handles layout, pagination, headers, footers, and styling automatically.

It supports vector text, tables with row spanning, page breaks, headers/footers, and custom fonts. Output is clean, searchable, and small in size.

// pdfmake: Declarative PDF
import pdfMake from 'pdfmake/build/pdfmake';
import pdfFonts from 'pdfmake/build/vfs_fonts';
pdfMake.vfs = pdfFonts.pdfMake.vfs;

const docDefinition = {
  content: [
    { text: 'Invoice', style: 'header' },
    { text: 'Customer: John Doe' },
    {
      table: {
        body: [
          ['Item', 'Price'],
          ['Widget', '$10'],
          ['Gadget', '$20']
        ]
      }
    }
  ],
  styles: {
    header: { fontSize: 18, bold: true }
  }
};

pdfMake.createPdf(docDefinition).download('invoice.pdf');

It also supports dynamic page headers/footers:

const docDefinition = {
  header: () => 'My Company',
  footer: (currentPage, pageCount) => `Page ${currentPage} of ${pageCount}`,
  content: [/* ... */]
};

✅ Use when: You need professional, print-ready documents with tables, consistent styling, and automatic pagination.
❌ Avoid when: Your source is raw HTML you can’t convert to structured data.

🔁 Input Source: HTML vs. Structured Data

This is the core architectural divide:

LibraryInput TypeRendering Method
html-pdfHTML (Node.js)PhantomJS (deprecated)
html2pdf.jsDOM ElementRasterized canvas
jsPDFCode (primitives)Vector drawing
pdfmakeJSON definitionVector layout engine

If your content lives as HTML in the DOM and you need a visual snapshot → html2pdf.js.
If you have structured data (e.g., API response) and need a polished PDF → pdfmake.
If you need pixel-perfect control over coordinates → jsPDF.

🌐 Runtime Environment

  • Browser only: html2pdf.js (by design)
  • Browser + Node.js: jsPDF, pdfmake (with minor setup in Node)
  • Node.js only (deprecated): html-pdf

Note: While jsPDF and pdfmake support Node.js, they don’t render HTML — they generate PDFs from code or definitions.

📄 Quality and File Size

  • html2pdf.js produces larger files because content is embedded as images.
  • jsPDF and pdfmake produce small, text-based PDFs that are searchable and scalable.
  • html-pdf output quality depends on PhantomJS, which lacks modern rendering capabilities.

🧩 Summary: When to Use Which

ScenarioRecommended Library
Convert on-screen DOM to PDF (e.g., “Export as PDF” button)html2pdf.js
Generate invoices, contracts, or reports from JSON datapdfmake
Create simple, code-defined PDFs (labels, certificates)jsPDF
Node.js HTML-to-PDF (new project)Not these — use Puppeteer
Node.js HTML-to-PDF (legacy project)Migrate away from html-pdf

🚫 Final Word on html-pdf

html-pdf should not be used in any new project. Its reliance on PhantomJS makes it obsolete, insecure, and incompatible with modern web standards. Replace it with headless Chrome via Puppeteer if you need server-side HTML-to-PDF.

For client-side needs, choose between html2pdf.js (for visual fidelity to DOM) and pdfmake/jsPDF (for structured, high-quality documents).

How to Choose: html-pdf vs html2pdf.js vs jspdf vs pdfmake

  • html-pdf:

    Do not use html-pdf in new projects — it is officially deprecated and relies on the unmaintained PhantomJS. It only works in Node.js and is unsuitable for client-side PDF generation. Existing projects should migrate to alternatives like Puppeteer or a modern browser-based solution.

  • html2pdf.js:

    Choose html2pdf.js when you need to convert existing HTML markup (e.g., a report, invoice, or dashboard) into a PDF directly in the browser with minimal setup. It’s ideal for capturing styled DOM elements as they appear visually, but be aware it uses rasterization (via html2canvas) for complex CSS, which may affect text quality and file size.

  • jspdf:

    Choose jsPDF when you need fine-grained control over PDF content and are generating documents programmatically rather than from HTML. It’s well-suited for simple reports, labels, or forms where layout can be built with code. However, it lacks native support for HTML/CSS rendering and advanced layout features like tables or page breaks without plugins.

  • pdfmake:

    Choose pdfmake when you need to build structured, print-ready PDFs with complex layouts (e.g., tables, headers, footers, columns) using a clean, declarative API. It excels in applications that generate invoices, contracts, or reports from structured data rather than raw HTML, and supports both browser and server-side rendering without rasterization.

README for html-pdf

node-html-pdf

HTML to PDF converter that uses phantomjs

image
Example Business Card
-> and its Source file

Example Receipt

Changelog

Have a look at the releases page: https://github.com/marcbachmann/node-html-pdf/releases

Installation

Install the html-pdf utility via npm:

$ npm install -g html-pdf

Command-line example

$ html-pdf test/businesscard.html businesscard.pdf

Code example

var fs = require('fs');
var pdf = require('html-pdf');
var html = fs.readFileSync('./test/businesscard.html', 'utf8');
var options = { format: 'Letter' };

pdf.create(html, options).toFile('./businesscard.pdf', function(err, res) {
  if (err) return console.log(err);
  console.log(res); // { filename: '/app/businesscard.pdf' }
});

API

var pdf = require('html-pdf');
pdf.create(html).toFile([filepath, ]function(err, res){
  console.log(res.filename);
});

pdf.create(html).toStream(function(err, stream){
  stream.pipe(fs.createWriteStream('./foo.pdf'));
});

pdf.create(html).toBuffer(function(err, buffer){
  console.log('This is a buffer:', Buffer.isBuffer(buffer));
});


// for backwards compatibility
// alias to pdf.create(html[, options]).toBuffer(callback)
pdf.create(html [, options], function(err, buffer){});

Footers and Headers

html-pdf can read the header or footer either out of the footer and header config object or out of the html source. You can either set a default header & footer or overwrite that by appending a page number (1 based index) to the id="pageHeader" attribute of a html tag.

You can use any combination of those tags. The library tries to find any element, that contains the pageHeader or pageFooter id prefix.

<div id="pageHeader">Default header</div>
<div id="pageHeader-first">Header on first page</div>
<div id="pageHeader-2">Header on second page</div>
<div id="pageHeader-3">Header on third page</div>
<div id="pageHeader-last">Header on last page</div>
...
<div id="pageFooter">Default footer</div>
<div id="pageFooter-first">Footer on first page</div>
<div id="pageFooter-2">Footer on second page</div>
<div id="pageFooter-last">Footer on last page</div>

Options

config = {

  // Export options
  "directory": "/tmp",       // The directory the file gets written into if not using .toFile(filename, callback). default: '/tmp'

  // Papersize Options: http://phantomjs.org/api/webpage/property/paper-size.html
  "height": "10.5in",        // allowed units: mm, cm, in, px
  "width": "8in",            // allowed units: mm, cm, in, px
  - or -
  "format": "Letter",        // allowed units: A3, A4, A5, Legal, Letter, Tabloid
  "orientation": "portrait", // portrait or landscape

  // Page options
  "border": "0",             // default is 0, units: mm, cm, in, px
  - or -
  "border": {
    "top": "2in",            // default is 0, units: mm, cm, in, px
    "right": "1in",
    "bottom": "2in",
    "left": "1.5in"
  },

  paginationOffset: 1,       // Override the initial pagination number
  "header": {
    "height": "45mm",
    "contents": '<div style="text-align: center;">Author: Marc Bachmann</div>'
  },
  "footer": {
    "height": "28mm",
    "contents": {
      first: 'Cover page',
      2: 'Second page', // Any page number is working. 1-based index
      default: '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>', // fallback value
      last: 'Last Page'
    }
  },


  // Rendering options
  "base": "file:///home/www/your-asset-path/", // Base path that's used to load files (images, css, js) when they aren't referenced using a host

  // Zooming option, can be used to scale images if `options.type` is not pdf
  "zoomFactor": "1", // default is 1

  // File options
  "type": "pdf",           // allowed file types: png, jpeg, pdf
  "quality": "75",         // only used for types png & jpeg

  // Script options
  "phantomPath": "./node_modules/phantomjs/bin/phantomjs", // PhantomJS binary which should get downloaded automatically
  "phantomArgs": [], // array of strings used as phantomjs args e.g. ["--ignore-ssl-errors=yes"]
  "localUrlAccess": false, // Prevent local file:// access by passing '--local-url-access=false' to phantomjs
                           // For security reasons you should keep the default value if you render arbritary html/js.
  "script": '/url',        // Absolute path to a custom phantomjs script, use the file in lib/scripts as example
  "timeout": 30000,        // Timeout that will cancel phantomjs, in milliseconds

  // Time we should wait after window load
  // accepted values are 'manual', some delay in milliseconds or undefined to wait for a render event
  "renderDelay": 1000,

  // HTTP Headers that are used for requests
  "httpHeaders": {
    // e.g.
    "Authorization": "Bearer ACEFAD8C-4B4D-4042-AB30-6C735F5BAC8B"
  },

  // To run Node application as Windows service
  "childProcessOptions": {
    "detached": true
  }

  // HTTP Cookies that are used for requests
  "httpCookies": [
    // e.g.
    {
      "name": "Valid-Cookie-Name", // required
      "value": "Valid-Cookie-Value", // required
      "domain": "localhost",
      "path": "/foo", // required
      "httponly": true,
      "secure": false,
      "expires": (new Date()).getTime() + (1000 * 60 * 60) // e.g. expires in 1 hour
    }
  ]

}

The full options object gets converted to JSON and will get passed to the phantomjs script as third argument.
There are more options concerning the paperSize, header & footer options inside the phantomjs script.