Creating invoices manually is tedious, error-prone, and simply does not scale. Whether you run a SaaS platform billing hundreds of customers or an e-commerce store processing thousands of orders, you need a way to generate professional invoices automatically. In this tutorial, we will walk through building a complete invoice generation system using HTML templates and the TongoRender PDF API.
The Problem: Manual Invoice Creation Does Not Scale
Many small businesses start with a spreadsheet or a Word document template for invoices. This works fine when you have five customers, but it breaks down quickly:
- Time cost — Each invoice takes 5-15 minutes to create manually. At 100 invoices per month, that is 8-25 hours of tedious data entry.
- Errors — Manual data entry leads to typos, wrong amounts, and missing line items. Invoice errors damage customer trust and create accounting headaches.
- Inconsistency — Different team members produce invoices with different formatting, making your business look unprofessional.
- No audit trail — Manual invoices make it hard to track what was sent, when, and to whom.
The Solution: Template-Based Generation via API
The modern approach is to separate the invoice template (the visual design) from the invoice data (customer info, line items, amounts). Your application generates the HTML by merging the template with data, then sends it to a PDF API to produce a polished document.
This approach gives you:
- Speed — Generate hundreds of invoices per minute.
- Accuracy — Data comes directly from your database, eliminating manual errors.
- Consistency — Every invoice uses the same template and branding.
- Automation — Trigger invoice generation on events like subscription renewal, order completion, or payment receipt.
Step 1: Design Your Invoice Template
Start with an HTML template that represents your invoice layout. Use CSS for styling — the PDF API will render it exactly as a browser would. Here is a clean, professional template:
const invoiceTemplate = (data) => `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: 'Helvetica Neue', Arial, sans-serif; color: #333; margin: 0; padding: 40px; }
.header { display: flex; justify-content: space-between; margin-bottom: 40px; }
.company-name { font-size: 24px; font-weight: bold; color: #1a1a2e; }
.invoice-info { text-align: right; }
.invoice-number { font-size: 18px; font-weight: bold; color: #6c63ff; }
table { width: 100%; border-collapse: collapse; margin: 30px 0; }
th { background: #f8f9fa; text-align: left; padding: 12px; border-bottom: 2px solid #dee2e6; }
td { padding: 12px; border-bottom: 1px solid #eee; }
.total-row { font-weight: bold; font-size: 18px; }
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #eee; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="header">
<div>
<div class="company-name">${data.company.name}</div>
<div>${data.company.address}</div>
<div>${data.company.email}</div>
</div>
<div class="invoice-info">
<div class="invoice-number">Invoice #${data.invoiceNumber}</div>
<div>Date: ${data.date}</div>
<div>Due: ${data.dueDate}</div>
</div>
</div>
<h3>Bill To</h3>
<p>${data.customer.name}<br/>${data.customer.address}<br/>${data.customer.email}</p>
<table>
<thead><tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Total</th></tr></thead>
<tbody>
${data.items.map(item => `
<tr>
<td>${item.description}</td>
<td>${item.quantity}</td>
<td>${item.unitPrice.toFixed(2)}</td>
<td>${(item.quantity * item.unitPrice).toFixed(2)}</td>
</tr>
`).join('')}
<tr class="total-row">
<td colspan="3">Total</td>
<td>${data.total.toFixed(2)}</td>
</tr>
</tbody>
</table>
<div class="footer">
<p>Payment terms: Net 30. Thank you for your business.</p>
</div>
</body>
</html>
`;
Step 2: Merge Data with the Template
Pull your invoice data from your database and pass it into the template function:
const invoiceData = {
company: {
name: 'Acme Corp',
address: '123 Business Ave, San Francisco, CA 94102',
email: 'billing@acme.com',
},
customer: {
name: 'Jane Smith',
address: '456 Client St, New York, NY 10001',
email: 'jane@example.com',
},
invoiceNumber: 'INV-2026-0042',
date: '2026-03-01',
dueDate: '2026-03-31',
items: [
{ description: 'Web Development Services', quantity: 40, unitPrice: 150 },
{ description: 'UI/UX Design', quantity: 20, unitPrice: 125 },
{ description: 'Hosting (Annual)', quantity: 1, unitPrice: 480 },
],
total: 40 * 150 + 20 * 125 + 480,
};
const html = invoiceTemplate(invoiceData);
Step 3: Generate the PDF with TongoRender
Now send the rendered HTML to the TongoRender API:
async function generateInvoicePDF(html) {
const response = await fetch('https://api.tongorender.io/v1/render/pdf', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer rf_live_your_api_key',
},
body: JSON.stringify({
html,
options: {
format: 'A4',
margin: { top: '15mm', bottom: '15mm', left: '15mm', right: '15mm' },
printBackground: true,
},
}),
});
if (!response.ok) {
throw new Error(`PDF generation failed: ${response.statusText}`);
}
return Buffer.from(await response.arrayBuffer());
}
// Usage:
const pdfBuffer = await generateInvoicePDF(html);
fs.writeFileSync(`invoices/${invoiceData.invoiceNumber}.pdf`, pdfBuffer);
Step 4: Automate the Workflow
In a real application, you would trigger invoice generation automatically. Here is an Express.js endpoint example:
app.post('/api/invoices/:orderId/pdf', async (req, res) => {
const order = await db.orders.findById(req.params.orderId);
const customer = await db.customers.findById(order.customerId);
const invoiceData = buildInvoiceData(order, customer);
const html = invoiceTemplate(invoiceData);
const pdfBuffer = await generateInvoicePDF(html);
// Store the PDF
await storage.upload(`invoices/${invoiceData.invoiceNumber}.pdf`, pdfBuffer);
// Send to customer
await emailService.send({
to: customer.email,
subject: `Invoice ${invoiceData.invoiceNumber}`,
attachments: [{ filename: `${invoiceData.invoiceNumber}.pdf`, content: pdfBuffer }],
});
res.json({ success: true, invoiceNumber: invoiceData.invoiceNumber });
});
Tips for Professional-Looking Invoices
Use a Consistent Color Palette
Pick two or three brand colors and use them consistently. Use your primary color for the invoice number and totals, a neutral color for body text, and a light shade for table headers.
Include Your Logo
Add your company logo as an image in the header. Use an absolute URL (e.g., https://yourcompany.com/logo.png) so the API can fetch it during rendering.
Handle Multi-Page Invoices
For invoices with many line items, use CSS page break rules to prevent awkward splits:
table { page-break-inside: auto; }
tr { page-break-inside: avoid; }
thead { display: table-header-group; } /* Repeats header on each page */
Add a Payment Link
Include a QR code or a short URL linking to your payment portal. This reduces friction and speeds up payment collection.
Include Legal Requirements
Depending on your jurisdiction, invoices may need to include tax IDs, VAT numbers, payment terms, or bank details. Build these into your template so they are never forgotten.
Conclusion
Programmatic invoice generation saves time, eliminates errors, and scales with your business. By combining HTML templates with a PDF API like TongoRender, you get full control over the design while offloading the rendering complexity to a managed service.
TongoRender makes invoice generation dead simple: send HTML, get a PDF. No browser to manage, no dependencies to install.
Try TongoRender free — Generate up to 100 invoices per month on the free plan.