Professional Invoices from HTML

Generating invoices is one of the most common PDF use cases. Instead of wrestling with PDF libraries that use coordinates and drawing commands, you can design invoices with familiar HTML and CSS, then convert them to pixel-perfect PDFs.

This tutorial gives you a complete, production-ready invoice template and shows how to generate PDFs with the ToolCenter.

The Invoice Template

Here’s a clean, professional invoice template:

<!DOCTYPE html>
<html>
<head>
<style>
  @page { size: A4; margin: 0; }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { font-family: 'Helvetica Neue', Arial, sans-serif; color: #333; font-size: 14px; }

  .invoice { padding: 50px; max-width: 800px; margin: 0 auto; }

  /* Header */
  .invoice-header { display: flex; justify-content: space-between; margin-bottom: 50px; }
  .company-info h1 { font-size: 28px; color: #2c3e50; margin-bottom: 5px; }
  .company-info p { color: #7f8c8d; font-size: 13px; line-height: 1.6; }
  .invoice-title { text-align: right; }
  .invoice-title h2 { font-size: 36px; color: #3498db; text-transform: uppercase; letter-spacing: 2px; }
  .invoice-title .invoice-number { font-size: 16px; color: #7f8c8d; margin-top: 5px; }

  /* Details Grid */
  .invoice-details { display: flex; justify-content: space-between; margin-bottom: 40px; }
  .detail-block h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 1px;
    color: #95a5a6; margin-bottom: 8px; }
  .detail-block p { line-height: 1.6; }

  /* Table */
  .invoice-table { width: 100%; border-collapse: collapse; margin-bottom: 30px; }
  .invoice-table th { background: #3498db; color: white; padding: 12px 15px; text-align: left;
    font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
  .invoice-table th:last-child { text-align: right; }
  .invoice-table td { padding: 12px 15px; border-bottom: 1px solid #ecf0f1; }
  .invoice-table td:last-child { text-align: right; }
  .invoice-table .item-desc { font-size: 12px; color: #95a5a6; }

  /* Totals */
  .invoice-totals { display: flex; justify-content: flex-end; }
  .totals-table { width: 280px; }
  .totals-table .row { display: flex; justify-content: space-between; padding: 8px 0;
    border-bottom: 1px solid #ecf0f1; }
  .totals-table .total { font-size: 18px; font-weight: bold; color: #2c3e50;
    border-bottom: 3px solid #3498db; padding: 12px 0; }

  /* Footer */
  .invoice-footer { margin-top: 60px; padding-top: 20px; border-top: 1px solid #ecf0f1;
    text-align: center; color: #95a5a6; font-size: 12px; }

  /* Payment Info */
  .payment-info { background: #f8f9fa; border-radius: 8px; padding: 20px; margin-top: 30px; }
  .payment-info h3 { font-size: 14px; color: #2c3e50; margin-bottom: 10px; }
  .payment-info p { font-size: 13px; color: #666; line-height: 1.6; }
</style>
</head>
<body>
<div class="invoice">
  <div class="invoice-header">
    <div class="company-info">
      <h1>Acme Corp</h1>
      <p>123 Business Street<br>San Francisco, CA 94102<br>[email protected]</p>
    </div>
    <div class="invoice-title">
      <h2>Invoice</h2>
      <div class="invoice-number">#INV-2026-0042</div>
    </div>
  </div>

  <div class="invoice-details">
    <div class="detail-block">
      <h3>Bill To</h3>
      <p><strong>Client Name</strong><br>456 Client Ave<br>New York, NY 10001<br>[email protected]</p>
    </div>
    <div class="detail-block">
      <h3>Invoice Date</h3>
      <p>February 19, 2026</p>
      <h3 style="margin-top:15px;">Due Date</h3>
      <p>March 19, 2026</p>
    </div>
  </div>

  <table class="invoice-table">
    <thead>
      <tr>
        <th>Description</th>
        <th>Qty</th>
        <th>Rate</th>
        <th>Amount</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Web Development Services<br><span class="item-desc">Frontend implementation — React</span></td>
        <td>40 hrs</td>
        <td>$150.00</td>
        <td>$6,000.00</td>
      </tr>
      <tr>
        <td>UI/UX Design<br><span class="item-desc">Dashboard redesign</span></td>
        <td>20 hrs</td>
        <td>$125.00</td>
        <td>$2,500.00</td>
      </tr>
      <tr>
        <td>API Integration<br><span class="item-desc">Third-party payment gateway</span></td>
        <td>15 hrs</td>
        <td>$175.00</td>
        <td>$2,625.00</td>
      </tr>
    </tbody>
  </table>

  <div class="invoice-totals">
    <div class="totals-table">
      <div class="row"><span>Subtotal</span><span>$11,125.00</span></div>
      <div class="row"><span>Tax (10%)</span><span>$1,112.50</span></div>
      <div class="row total"><span>Total</span><span>$12,237.50</span></div>
    </div>
  </div>

  <div class="payment-info">
    <h3>Payment Information</h3>
    <p>Bank: First National Bank | Account: 1234567890 | Routing: 021000021<br>
    Please include invoice number in the payment reference.</p>
  </div>

  <div class="invoice-footer">
    <p>Thank you for your business! | Questions? Contact [email protected]</p>
  </div>
</div>
</body>
</html>

Generating the PDF

Node.js

const axios = require('axios');
const fs = require('fs');

async function generateInvoice(invoiceHtml) {
  const response = await axios.post(
    'https://api.toolcenter.dev/v1/pdf',
    {
      html: invoiceHtml,
      format: 'A4',
      printBackground: true,
      margin: { top: '0mm', bottom: '0mm', left: '0mm', right: '0mm' },
    },
    {
      headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
      responseType: 'arraybuffer',
    }
  );

  return Buffer.from(response.data);
}

// Generate and save
const html = fs.readFileSync('./invoice-template.html', 'utf-8');
const pdf = await generateInvoice(html);
fs.writeFileSync('invoice-2026-0042.pdf', pdf);

Python

import requests

def generate_invoice(html):
    response = requests.post(
        'https://api.toolcenter.dev/v1/pdf',
        json={
            'html': html,
            'format': 'A4',
            'printBackground': True,
            'margin': {'top': '0mm', 'bottom': '0mm', 'left': '0mm', 'right': '0mm'},
        },
        headers={'Authorization': 'Bearer YOUR_API_KEY'}
    )
    return response.content

with open('invoice-template.html') as f:
    html = f.read()

pdf = generate_invoice(html)
with open('invoice.pdf', 'wb') as f:
    f.write(pdf)

Making It Dynamic

Use a template engine to populate invoices with real data:

const Handlebars = require('handlebars');

Handlebars.registerHelper('currency', (value) => {
  return `$${parseFloat(value).toLocaleString('en-US', { minimumFractionDigits: 2 })}`;
});

function renderInvoice(data) {
  const template = Handlebars.compile(templateSource);

  const subtotal = data.items.reduce((sum, item) => sum + item.quantity * item.rate, 0);
  const tax = subtotal * (data.taxRate || 0.1);
  const total = subtotal + tax;

  return template({
    ...data,
    subtotal,
    tax,
    total,
    invoiceDate: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }),
  });
}

// Usage
const html = renderInvoice({
  invoiceNumber: 'INV-2026-0042',
  company: { name: 'Acme Corp', address: '123 Business St', email: '[email protected]' },
  client: { name: 'Client Name', address: '456 Client Ave', email: '[email protected]' },
  items: [
    { description: 'Web Development', detail: 'React frontend', quantity: 40, rate: 150 },
    { description: 'Design', detail: 'Dashboard redesign', quantity: 20, rate: 125 },
  ],
  taxRate: 0.1,
  dueDate: 'March 19, 2026',
});

const pdf = await generateInvoice(html);

Customizing the Design

Change the Color Scheme

Replace the accent color throughout the CSS:

/* Blue (default) */
.invoice-title h2 { color: #3498db; }
.invoice-table th { background: #3498db; }

/* Green */
.invoice-title h2 { color: #27ae60; }
.invoice-table th { background: #27ae60; }

/* Dark/Professional */
.invoice-title h2 { color: #2c3e50; }
.invoice-table th { background: #2c3e50; }
<div class="company-info">
  <img src="https://yoursite.com/logo.png" style="height:50px;margin-bottom:10px;" />
  <h1>Acme Corp</h1>
</div>

Add a Watermark

.invoice::before {
  content: 'PAID';
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-45deg);
  font-size: 120px;
  color: rgba(46, 204, 113, 0.1);
  font-weight: bold;
  z-index: 0;
  pointer-events: none;
}

Batch Invoice Generation

Generate invoices for all clients at once:

async function generateMonthlyInvoices(clients) {
  const results = [];

  for (const client of clients) {
    const html = renderInvoice(client);
    const pdf = await generateInvoice(html);

    const filename = `invoice-${client.invoiceNumber}.pdf`;
    fs.writeFileSync(`./invoices/${filename}`, pdf);

    results.push({ client: client.name, filename });
  }

  return results;
}

Conclusion

HTML and CSS give you complete control over invoice design without learning PDF-specific APIs. The ToolCenter PDF API converts your templates into print-ready PDFs with perfect rendering. Start with the template above, customize it to match your brand, and automate invoice generation for your entire client base.