DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

How to generate a PDF invoice from HTML in Node.js (10 lines of code)

How to Generate a PDF Invoice from HTML in Node.js (10 Lines of Code)

PDF libraries are a tax on developer time. You install one, fight the layout engine, discover it doesn't support your CSS, and spend a day rebuilding your invoice template in a proprietary DSL. There's a better way.

If your invoice already exists as HTML — or you can render it to HTML — you can turn it into a print-perfect PDF with a single API call.

The approach

PageBolt's /v1/pdf endpoint takes a URL or raw HTML and returns a PDF. It renders in a real browser, so your CSS grid, custom fonts, and @media print styles all work exactly as expected.

const fs = require('fs');

const html = `<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Inter, sans-serif; padding: 48px; color: #111; }
    .header { display: flex; justify-content: space-between; margin-bottom: 48px; }
    .amount { font-size: 32px; font-weight: 700; }
    table { width: 100%; border-collapse: collapse; }
    td, th { padding: 12px 0; border-bottom: 1px solid #eee; text-align: left; }
    .total { font-weight: 700; font-size: 18px; }
  </style>
</head>
<body>
  <div class="header">
    <div><strong>Acme Corp</strong><br>123 Main St, San Francisco CA</div>
    <div>Invoice #1042<br>Due: March 15, 2026</div>
  </div>
  <table>
    <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
    <tr><td>Pro Plan — March</td><td>1</td><td>$99.00</td></tr>
    <tr><td>Extra seats (3)</td><td>3</td><td>$45.00</td></tr>
    <tr><td class="total" colspan="2">Total</td><td class="total">$144.00</td></tr>
  </table>
</body>
</html>`;

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

fs.writeFileSync('invoice-1042.pdf', Buffer.from(await response.arrayBuffer()));
Enter fullscreen mode Exit fullscreen mode

That's the complete implementation. The PDF is written to disk. Swap in your real template and you're done.

Passing a URL instead of raw HTML

If your app already renders invoices at a URL (e.g., /invoices/1042), pass the URL directly:

body: JSON.stringify({
  url: 'https://yourapp.com/invoices/1042',
  format: 'A4',
  printBackground: true
})
Enter fullscreen mode Exit fullscreen mode

PageBolt will fetch and render the page. For pages behind authentication, pass cookies:

body: JSON.stringify({
  url: 'https://yourapp.com/invoices/1042',
  cookies: ['session=abc123; Domain=yourapp.com'],
  format: 'A4',
  printBackground: true
})
Enter fullscreen mode Exit fullscreen mode

Print-specific styling

Use @media print in your CSS to hide UI chrome (nav, buttons, banners) from the PDF:

@media print {
  nav, .sidebar, .no-print { display: none; }
  body { padding: 0; }
}
Enter fullscreen mode Exit fullscreen mode

PageBolt renders with mediaType: "print" by default for PDF generation, so these rules apply automatically.

Margins and page format

body: JSON.stringify({
  html,
  format: 'Letter',        // A4, Letter, Legal, A3, A5
  margin: '1in',           // all sides, or { top: '1in', bottom: '0.5in', ... }
  printBackground: true    // include background colors/images
})
Enter fullscreen mode Exit fullscreen mode

Letter and A4 are the most common. Set margin to '0' if your HTML template already includes its own padding.


No PDF library. No Puppeteer setup. No Docker container to maintain. One fetch call, one file on disk.

Try it free — 100 requests/month, no credit card. → pagebolt.dev

Top comments (0)