html-pdf Migration: 5 Common Pitfalls and How to Fix Them
Moving from html-pdf to PageBolt? Here are the 5 most common migration pitfalls and how to solve them.
You've decided to migrate from html-pdf to PageBolt. Good call. But you've hit a wall. Your PDFs look different. CSS isn't rendering. Callbacks are now promises. Here are the 5 biggest migration pitfalls and how to fix them.
Pitfall #1: CSS Rendering Differences (Grid, Flexbox, Z-Index)
The Problem
Your HTML/CSS worked perfectly in html-pdf. Now with PageBolt, CSS Grid layouts collapse. Flexbox behaves differently. Z-index stacking breaks.
Why it happens
html-pdf used PhantomJS (2014 WebKit). PageBolt uses a modern Chromium engine. Modern CSS works differently.
The Fix
Before (html-pdf):
.container {
display: flex;
justify-content: space-between;
gap: 20px;
}
After (PageBolt):
Same CSS usually works, but test edge cases:
- Ensure all parent containers have explicit
height - Use
flex-wrap: wrapif content overflows - Test Z-index with explicit
position: relative/absolute/fixed
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: htmlContent,
format: 'A4',
margin: '0.5in',
// Add explicit viewport width to match your HTML
width: 1200,
}),
});
Pitfall #2: Async/Await vs Callback Hell
The Problem
You're migrating from html-pdf callbacks to promises, but your error handling is all wrong.
Before (html-pdf):
pdf.create(html, options).toFile('invoice.pdf', (err, res) => {
if (err) {
console.error('Failed:', err);
return res.status(500).send('PDF generation failed');
}
res.download(res.filename);
});
After (PageBolt):
try {
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 }),
});
if (!response.ok) {
throw new Error(`PDF generation failed: ${response.status}`);
}
const buffer = await response.arrayBuffer();
res.setHeader('Content-Type', 'application/pdf');
res.send(Buffer.from(buffer));
} catch (error) {
console.error('PDF error:', error);
res.status(500).send('Failed to generate PDF');
}
Key difference: PageBolt returns HTTP status codes. Check response.ok before processing the buffer.
Pitfall #3: Timeout and ENOMEM Errors on Bulk Operations
The Problem
Generating 1,000 PDFs in parallel crashes with "ENOMEM" or timeout errors.
Why it happens
You're sending all 1,000 requests at once. Each one waits for a response. The system runs out of memory.
The Fix
Use a concurrency limit. Generate PDFs in batches of 5–10:
async function generatePDFsBatch(invoices, batchSize = 5) {
const results = [];
for (let i = 0; i < invoices.length; i += batchSize) {
const batch = invoices.slice(i, i + batchSize);
const promises = batch.map(async (invoice) => {
const html = renderTemplate(invoice);
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' }),
});
if (!response.ok) throw new Error(`Failed: ${response.status}`);
return response.arrayBuffer();
});
const batchResults = await Promise.all(promises);
results.push(...batchResults);
}
return results;
}
Process 5 at a time instead of 1,000 at once.
Pitfall #4: Font Loading Issues
The Problem
Your custom fonts (Google Fonts, Typekit, @font-face) aren't loading in the PDF.
The Fix
Use web-safe fonts or embed fonts directly in the HTML:
<style>
@font-face {
font-family: 'CustomFont';
src: url('data:font/woff2;base64,...') format('woff2');
}
body {
font-family: 'CustomFont', Arial, sans-serif;
}
</style>
Or use system fonts: Arial, Georgia, Courier New, Verdana.
Pitfall #5: Missing Images and External Resources
The Problem
Images in your HTML show as broken boxes in the PDF.
The Fix
Use absolute URLs and ensure they're publicly accessible:
<!-- ❌ Won't work (relative path) -->
<img src="/images/logo.png" />
<!-- ✅ Works (absolute URL) -->
<img src="https://example.com/images/logo.png" />
<!-- ✅ Also works (base64 inline) -->
<img src="data:image/png;base64,iVBORw0KGgo..." />
Checklist
- Test CSS Grid and Flexbox layouts
- Switch to async/await error handling
- Implement batch processing for bulk PDFs (max 5–10 concurrent)
- Use web-safe fonts or embedded fonts
- Convert image paths to absolute URLs
- Test with your actual invoice templates before going live
Summary
Migration is straightforward once you understand these 5 pitfalls:
- Modern CSS rendering (test your layouts)
- Async/await error handling (check
response.ok) - Concurrency limits (batch your requests)
- Font compatibility (use web-safe fonts)
- Image paths (use absolute URLs)
Your html-pdf migration is probably 95% done. These fixes handle the last 5%.
Finish your html-pdf migration today
100 requests/month free. No credit card. Works on Node 18+, serverless, containers.
Get API key free →