html-pdf npm is Abandoned: Here's the Modern Alternative
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:
- ❌ Async callback hell (no Promises)
- ❌ Crashes randomly on complex HTML/CSS
- ❌ No support for CSS Grid, Flexbox, modern features
- ❌ Slow (5-10 seconds per PDF on average)
- ❌ Breaks on Node 18+
- ❌ No built-in error recovery
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:
- ✅ Async/await (modern JavaScript)
- ✅ Handles CSS Grid, Flexbox, modern CSS
- ✅ Fast (under 1 second)
- ✅ Works on all Node versions
- ✅ Built-in error handling
- ✅ No system dependencies
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
- [ ] Uninstall
html-pdfand PhantomJS - [ ] Install
node-fetch(if needed) - [ ] Get API key from PageBolt dashboard
- [ ] Update code to use fetch +
/api/v1/pdf - [ ] Test with existing HTML templates
- [ ] Deploy and monitor
- [ ] Remove old npm scripts that reference html-pdf
Summary
- ❌
html-pdfis unmaintained and broken on Node 18+ - ❌ PhantomJS is dead and insecure
- ✅ PageBolt replaces it with a modern, fast API
- ✅ Migration takes 10 minutes
- ✅ Zero system dependencies
- ✅ Works with existing HTML/CSS
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.
Get started free — 100 requests/month, no credit card required →
Top comments (0)