html-pdf Migration: 5 Common Pitfalls and How to Fix 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%.
Need help? Try PageBolt free — 100 requests/month, no credit card. Get started →
Top comments (0)