Tutorial Mar 26, 2026

html-pdf npm is Abandoned: Here's the Modern Alternative

html-pdf hasn't been maintained since 2019 and breaks on Node 18+. Here's how to migrate to a modern PDF API in under 10 minutes.

If you're using the html-pdf npm package, you probably already know it's broken. It hasn't been maintained since 2019. It fails on Node 18+. It's built on PhantomJS, which is also abandoned. And if you're trying to install it now, you're hitting permission errors or segmentation faults.

You're not alone. Thousands of developers are stuck on html-pdf because migrating seemed hard. It isn't.

Why html-pdf is Broken

html-pdf relies on PhantomJS — a headless browser that was abandoned in 2018. PhantomJS doesn't work on modern Node versions, doesn't support modern JavaScript, and has zero security updates.

The html-pdf package itself hasn't been updated in years. Issues pile up on GitHub. PRs go unreviewed. It's unmaintained.

If you're on Node 18+, you're probably seeing:

npm ERR! ERR! code EBUILD
npm ERR! node-gyp rebuild
npm ERR! gyp ERR! configure error

This is html-pdf trying to compile native bindings that don't work on modern systems.

Before: html-pdf Setup

Here's the current approach:

npm install html-pdf

Then in your code:

const pdf = require('html-pdf');

const htmlContent = `
  <h1>Invoice #12345</h1>
  <p>Amount: $100</p>
`;

const options = {
  width: '8.5in',
  height: '11in',
  format: 'Letter',
};

pdf.create(htmlContent, options).toFile('invoice.pdf', (err, res) => {
  if (err) {
    console.log('PDF generation failed:', err);
    return;
  }
  console.log('PDF created:', res.filename);
});

Problems:

After: PageBolt PDF API

Modern approach using PageBolt:

npm uninstall html-pdf
npm install node-fetch

Then in your code:

const fetch = require('node-fetch');

const htmlContent = `
  <h1>Invoice #12345</h1>
  <p>Amount: $100</p>
`;

const response = await fetch('https://pagebolt.dev/api/v1/pdf', {
  method: 'POST',
  headers: {
    'x-api-key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    html: htmlContent,
    format: 'A4',
    margin: '0.5in',
  }),
});

if (!response.ok) {
  throw new Error(`PDF generation failed: ${response.status}`);
}

const buffer = await response.arrayBuffer();
fs.writeFileSync('invoice.pdf', Buffer.from(buffer));
console.log('PDF created successfully');

Better:

Migration in 3 Steps

Step 1: Uninstall html-pdf

npm uninstall html-pdf

This removes PhantomJS, native bindings, and all the broken dependencies.

Step 2: Install fetch (if needed)

npm install node-fetch

If you're on Node 18+, you have fetch built-in. For older Node, install node-fetch.

Step 3: Replace the code

Old (html-pdf callback):

pdf.create(html, options).toFile('output.pdf', (err, res) => {
  if (err) console.error(err);
  else console.log('Done:', res.filename);
});

New (PageBolt promise):

const response = await fetch('https://pagebolt.dev/api/v1/pdf', {
  method: 'POST',
  headers: { 'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
  body: JSON.stringify({ html, format: 'A4' }),
});
const buffer = await response.arrayBuffer();
fs.writeFileSync('output.pdf', Buffer.from(buffer));

Real-World Example: Invoice Generation

Before (html-pdf, broken):

const pdf = require('html-pdf');
const handlebars = require('handlebars');

function generateInvoice(invoiceData) {
  const template = handlebars.compile(invoiceTemplate);
  const html = template(invoiceData);

  return new Promise((resolve, reject) => {
    pdf.create(html).toFile(`invoice-${invoiceData.id}.pdf`, (err, res) => {
      if (err) reject(err);
      else resolve(res.filename);
    });
  });
}

After (PageBolt, modern):

const fetch = require('node-fetch');
const handlebars = require('handlebars');

async function generateInvoice(invoiceData) {
  const template = handlebars.compile(invoiceTemplate);
  const html = template(invoiceData);

  const response = await fetch('https://pagebolt.dev/api/v1/pdf', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.PAGEBOLT_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ html, format: 'A4', margin: '0.5in' }),
  });

  if (!response.ok) throw new Error(`PDF failed: ${response.status}`);

  const buffer = await response.arrayBuffer();
  const filename = `invoice-${invoiceData.id}.pdf`;
  fs.writeFileSync(filename, Buffer.from(buffer));
  return filename;
}

Cleaner. Faster. Works.

Performance Comparison

Metric html-pdf PageBolt
PDF Generation 5-10 seconds 0.5-1 second
Node 18+ Support ❌ Broken ✅ Yes
CSS Support Basic Full (Grid, Flexbox, modern)
Memory Usage 150MB+ < 50MB
Error Handling Poor Detailed status codes
Maintenance 🪦 Dead ✅ Active

Pricing

Plan Requests/Month Cost Best For
Free 100 $0 Testing & small projects
Starter 5,000 $29 500 invoices/month
Growth 25,000 $79 2,000+ invoices/month
Scale 100,000 $199 Enterprise billing systems

Migration Checklist

Summary

If you're still using html-pdf, migrate today. Your invoices will generate 10x faster, your code will be cleaner, and you'll never hit another Node compatibility error.

Migrate from html-pdf in 10 minutes

100 requests/month free. No credit card. Works on Node 18+, serverless, containers — everywhere html-pdf doesn't.

Get API key free →