Invoice Template
Build professional, calculation-ready invoice PDFs with dynamic line items, automatic totals, and customizable branding.
Learning Objectives
By the end of this article, you’ll be able to:
- Create professional invoice templates
- Implement line items with data binding
- Calculate subtotals, tax, and totals automatically
- Add company branding and styling
- Handle payment terms and notes
- Generate invoices from C# code
Complete Invoice Template
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>{{invoice.number}}</title>
<style>
body {
font-family: 'Helvetica', 'Arial', sans-serif;
font-size: 11pt;
line-height: 1.6;
color: #333;
margin: 40pt;
}
/* Header styling */
.invoice-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
border-bottom: 2pt solid #2563eb;
padding-bottom: 15pt;
margin-bottom: 30pt;
}
.company-logo {
height: 50pt;
}
.invoice-title {
text-align: right;
}
.invoice-title h1 {
font-size: 28pt;
color: #2563eb;
margin: 0;
font-weight: bold;
}
.invoice-title p {
margin: 5pt 0;
font-size: 12pt;
color: #666;
}
/* Company and customer info */
.info-section {
display: flex;
justify-content: space-between;
margin-bottom: 30pt;
}
.company-info, .customer-info {
width: 45%;
}
.company-info h3, .customer-info h3 {
font-size: 12pt;
color: #2563eb;
margin-bottom: 8pt;
font-weight: 600;
}
.company-info p, .customer-info p {
margin: 4pt 0;
font-size: 10pt;
line-height: 1.4;
}
/* Invoice details */
.invoice-details {
background-color: #f9fafb;
padding: 15pt;
margin-bottom: 30pt;
border: 1pt solid #e5e7eb;
border-radius: 4pt;
}
.invoice-details table {
width: 100%;
border: none;
}
.invoice-details td {
padding: 4pt 8pt;
border: none;
}
.invoice-details td:first-child {
font-weight: 600;
color: #555;
width: 150pt;
}
/* Line items table */
.line-items {
width: 100%;
border-collapse: collapse;
margin-bottom: 20pt;
}
.line-items thead {
background-color: #2563eb;
color: white;
}
.line-items th {
padding: 12pt;
text-align: left;
font-weight: bold;
font-size: 10pt;
text-transform: uppercase;
}
.line-items th.right {
text-align: right;
}
.line-items td {
padding: 10pt 12pt;
border-bottom: 1pt solid #e5e7eb;
}
.line-items td.right {
text-align: right;
}
.line-items tbody tr:nth-child(even) {
background-color: #f9fafb;
}
.line-items tbody tr:hover {
background-color: #eff6ff;
}
/* Totals section */
.totals-section {
width: 300pt;
margin-left: auto;
margin-bottom: 30pt;
}
.totals-section table {
width: 100%;
border-collapse: collapse;
}
.totals-section td {
padding: 8pt 12pt;
border-bottom: 1pt solid #e5e7eb;
}
.totals-section td:first-child {
font-weight: 600;
}
.totals-section td:last-child {
text-align: right;
}
.total-row {
background-color: #2563eb;
color: white;
font-size: 13pt;
font-weight: bold;
}
.total-row td {
border-bottom: none;
padding: 12pt;
}
/* Payment terms and notes */
.payment-terms {
background-color: #fffbeb;
border-left: 4pt solid #f59e0b;
padding: 15pt;
margin-bottom: 20pt;
}
.payment-terms h3 {
margin: 0 0 8pt 0;
font-size: 12pt;
color: #d97706;
}
.payment-terms p {
margin: 4pt 0;
font-size: 10pt;
line-height: 1.5;
}
/* Footer */
.invoice-footer {
text-align: center;
margin-top: 40pt;
padding-top: 15pt;
border-top: 1pt solid #e5e7eb;
font-size: 9pt;
color: #666;
}
/* Notes section */
.notes {
margin-bottom: 20pt;
}
.notes h3 {
font-size: 12pt;
color: #555;
margin-bottom: 8pt;
}
.notes p {
font-size: 10pt;
line-height: 1.5;
color: #666;
}
</style>
</head>
<body>
<!-- Header -->
<div class="invoice-header">
<div>
<img src="{{company.logo}}" class="company-logo" alt="Company Logo" />
</div>
<div class="invoice-title">
<h1>INVOICE</h1>
<p>{{invoice.number}}</p>
</div>
</div>
<!-- Company and Customer Info -->
<div class="info-section">
<div class="company-info">
<h3>From</h3>
<p><strong>{{company.name}}</strong></p>
<p>{{company.address}}</p>
<p>{{company.city}}, {{company.state}} {{company.zip}}</p>
<p>Phone: {{company.phone}}</p>
<p>Email: {{company.email}}</p>
</div>
<div class="customer-info">
<h3>Bill To</h3>
<p><strong>{{customer.name}}</strong></p>
{{#if customer.company}}
<p>{{customer.company}}</p>
{{/if}}
<p>{{customer.address}}</p>
<p>{{customer.city}}, {{customer.state}} {{customer.zip}}</p>
{{#if customer.email}}
<p>Email: {{customer.email}}</p>
{{/if}}
</div>
</div>
<!-- Invoice Details -->
<div class="invoice-details">
<table>
<tr>
<td>Invoice Number:</td>
<td>{{invoice.number}}</td>
<td>Invoice Date:</td>
<td>{{invoice.date}}</td>
</tr>
<tr>
<td>Purchase Order:</td>
<td>{{invoice.poNumber}}</td>
<td>Due Date:</td>
<td>{{invoice.dueDate}}</td>
</tr>
</table>
</div>
<!-- Line Items -->
<table class="line-items">
<thead>
<tr>
<th>Description</th>
<th class="right">Quantity</th>
<th class="right">Unit Price</th>
<th class="right">Amount</th>
</tr>
</thead>
<tbody>
{{#each invoice.items}}
<tr>
<td>
<strong>{{this.description}}</strong>
{{#if this.details}}
<br /><span style="font-size: 9pt; color: #666;">{{this.details}}</span>
{{/if}}
</td>
<td class="right">{{this.quantity}}</td>
<td class="right">${{format(this.price, '0.00')}}</td>
<td class="right">${{format(calc(this.quantity, '*', this.price), '0.00')}}</td>
</tr>
{{/each}}
</tbody>
</table>
<!-- Totals -->
<div class="totals-section">
<table>
<tr>
<td>Subtotal:</td>
<td>${{format(invoice.subtotal, '0.00')}}</td>
</tr>
{{#if invoice.discount}}
<tr>
<td>Discount ({{invoice.discountPercent}}%):</td>
<td>-${{format(invoice.discount, '0.00')}}</td>
</tr>
{{/if}}
<tr>
<td>Tax ({{format(calc(invoice.taxRate, '*', 100), '0.0')}}%):</td>
<td>${{format(invoice.tax, '0.00')}}</td>
</tr>
{{#if invoice.shipping}}
<tr>
<td>Shipping:</td>
<td>${{format(invoice.shipping, '0.00')}}</td>
</tr>
{{/if}}
<tr class="total-row">
<td>Total Due:</td>
<td>${{format(invoice.total, '0.00')}}</td>
</tr>
</table>
</div>
<!-- Payment Terms -->
<div class="payment-terms">
<h3>Payment Terms</h3>
<p>{{invoice.paymentTerms}}</p>
{{#if invoice.paymentInstructions}}
<p>{{invoice.paymentInstructions}}</p>
{{/if}}
</div>
<!-- Notes -->
{{#if invoice.notes}}
<div class="notes">
<h3>Notes</h3>
<p>{{invoice.notes}}</p>
</div>
{{/if}}
<!-- Footer -->
<div class="invoice-footer">
<p>{{company.name}} | {{company.phone}} | {{company.email}}</p>
<p>{{company.website}}</p>
</div>
</body>
</html>
C# Invoice Generator
using Scryber.Components;
using Scryber.PDF;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
public class InvoiceGenerator
{
private readonly string _templatePath;
public InvoiceGenerator(string templatePath)
{
_templatePath = templatePath;
}
public void GenerateInvoice(InvoiceData invoiceData, Stream output)
{
// Calculate totals
CalculateTotals(invoiceData);
// Load template
var doc = Document.ParseDocument(_templatePath);
// Set metadata
doc.Info.Title = $"Invoice {invoiceData.Invoice.Number}";
doc.Info.Author = invoiceData.Company.Name;
doc.Info.Subject = $"Invoice for {invoiceData.Customer.Name}";
doc.Info.CreationDate = DateTime.Now;
// Bind data
doc.Params["company"] = invoiceData.Company;
doc.Params["customer"] = invoiceData.Customer;
doc.Params["invoice"] = invoiceData.Invoice;
// Generate PDF
doc.SaveAsPDF(output);
}
private void CalculateTotals(InvoiceData invoiceData)
{
var invoice = invoiceData.Invoice;
// Calculate subtotal
invoice.Subtotal = invoice.Items.Sum(item => item.Quantity * item.Price);
// Apply discount if any
if (invoice.DiscountPercent > 0)
{
invoice.Discount = invoice.Subtotal * (invoice.DiscountPercent / 100);
}
// Calculate taxable amount
var taxableAmount = invoice.Subtotal - invoice.Discount;
// Calculate tax
invoice.Tax = taxableAmount * invoice.TaxRate;
// Calculate total
invoice.Total = taxableAmount + invoice.Tax + invoice.Shipping;
}
}
// Data models
public class InvoiceData
{
public CompanyInfo Company { get; set; }
public CustomerInfo Customer { get; set; }
public Invoice Invoice { get; set; }
}
public class CompanyInfo
{
public string Name { get; set; }
public string Logo { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Website { get; set; }
}
public class CustomerInfo
{
public string Name { get; set; }
public string Company { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Email { get; set; }
}
public class Invoice
{
public string Number { get; set; }
public string Date { get; set; }
public string DueDate { get; set; }
public string PoNumber { get; set; }
public List<InvoiceItem> Items { get; set; } = new List<InvoiceItem>();
public decimal Subtotal { get; set; }
public decimal DiscountPercent { get; set; }
public decimal Discount { get; set; }
public decimal TaxRate { get; set; }
public decimal Tax { get; set; }
public decimal Shipping { get; set; }
public decimal Total { get; set; }
public string PaymentTerms { get; set; }
public string PaymentInstructions { get; set; }
public string Notes { get; set; }
}
public class InvoiceItem
{
public string Description { get; set; }
public string Details { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}
Usage Example
// Create invoice data
var invoiceData = new InvoiceData
{
Company = new CompanyInfo
{
Name = "Acme Corporation",
Logo = "./images/acme-logo.png",
Address = "123 Main Street",
City = "New York",
State = "NY",
Zip = "10001",
Phone = "(555) 123-4567",
Email = "billing@acme.com",
Website = "www.acme.com"
},
Customer = new CustomerInfo
{
Name = "John Smith",
Company = "Smith Industries",
Address = "456 Oak Avenue",
City = "Boston",
State = "MA",
Zip = "02101",
Email = "john@smithind.com"
},
Invoice = new Invoice
{
Number = "INV-2025-001",
Date = "October 17, 2025",
DueDate = "November 17, 2025",
PoNumber = "PO-12345",
Items = new List<InvoiceItem>
{
new InvoiceItem
{
Description = "Professional Services",
Details = "Consulting services for Q4 2025",
Quantity = 40,
Price = 150.00m
},
new InvoiceItem
{
Description = "Software License",
Details = "Annual subscription",
Quantity = 1,
Price = 2500.00m
},
new InvoiceItem
{
Description = "Training Session",
Details = "On-site team training",
Quantity = 2,
Price = 500.00m
}
},
DiscountPercent = 0,
TaxRate = 0.08m,
Shipping = 0,
PaymentTerms = "Net 30 days. Payment due within 30 days of invoice date.",
PaymentInstructions = "Please make checks payable to Acme Corporation or pay via bank transfer to account #12345678.",
Notes = "Thank you for your business!"
}
};
// Generate invoice
var generator = new InvoiceGenerator("invoice-template.html");
using (var output = new FileStream("invoice-2025-001.pdf", FileMode.Create))
{
generator.GenerateInvoice(invoiceData, output);
Console.WriteLine($"Invoice generated: {output.Name}");
Console.WriteLine($"Total amount: ${invoiceData.Invoice.Total:0.00}");
}
Step-by-Step Breakdown
1. Header Section
<div class="invoice-header">
<div>
<img src="{{company.logo}}" class="company-logo" />
</div>
<div class="invoice-title">
<h1>INVOICE</h1>
<p>{{invoice.number}}</p>
</div>
</div>
Features:
- Company logo on left
- Invoice title and number on right
- Flexbox layout for alignment
- Bottom border for separation
2. Company and Customer Information
<div class="info-section">
<div class="company-info">
<h3>From</h3>
<p><strong>{{company.name}}</strong></p>
<p>{{company.address}}</p>
</div>
<div class="customer-info">
<h3>Bill To</h3>
<p><strong>{{customer.name}}</strong></p>
{{#if customer.company}}
<p>{{customer.company}}</p>
{{/if}}
</div>
</div>
Features:
- Side-by-side layout
- Conditional customer company name
- Clear visual hierarchy
3. Line Items Table
<table class="line-items">
<thead>
<tr>
<th>Description</th>
<th class="right">Quantity</th>
<th class="right">Unit Price</th>
<th class="right">Amount</th>
</tr>
</thead>
<tbody>
{{#each invoice.items}}
<tr>
<td>
<strong>{{this.description}}</strong>
{{#if this.details}}
<br /><span style="font-size: 9pt;">{{this.details}}</span>
{{/if}}
</td>
<td class="right">{{this.quantity}}</td>
<td class="right">${{format(this.price, '0.00')}}</td>
<td class="right">${{format(calc(this.quantity, '*', this.price), '0.00')}}</td>
</tr>
{{/each}}
</tbody>
</table>
Features:
- Dynamic rows with ``
- Automatic calculation: quantity × price
- Optional item details
- Professional styling with alternating rows
4. Totals Calculation
<div class="totals-section">
<table>
<tr>
<td>Subtotal:</td>
<td>${{format(invoice.subtotal, '0.00')}}</td>
</tr>
<tr>
<td>Tax ({{format(calc(invoice.taxRate, '*', 100), '0.0')}}%):</td>
<td>${{format(invoice.tax, '0.00')}}</td>
</tr>
<tr class="total-row">
<td>Total Due:</td>
<td>${{format(invoice.total, '0.00')}}</td>
</tr>
</table>
</div>
Features:
- Subtotal, tax, and total
- Optional discount and shipping
- Highlighted total row
- Right-aligned for readability
Common Variations
Variation 1: Simple Invoice (Minimal)
Remove optional sections for a cleaner look:
<!-- Remove discount section -->
<!-- Remove shipping section -->
<!-- Remove notes section -->
Variation 2: Detailed Invoice (Itemized)
Add more item details:
<tr>
<td>
<strong>{{this.description}}</strong>
<br /><span>SKU: {{this.sku}}</span>
<br /><span>{{this.details}}</span>
</td>
<td class="right">{{this.quantity}} {{this.unit}}</td>
<td class="right">${{format(this.price, '0.00')}}</td>
<td class="right">{{this.discountPercent}}%</td>
<td class="right">${{format(this.lineTotal, '0.00')}}</td>
</tr>
Variation 3: Service Invoice (Hourly)
For hourly billing:
<table class="line-items">
<thead>
<tr>
<th>Service Description</th>
<th class="right">Hours</th>
<th class="right">Rate</th>
<th class="right">Amount</th>
</tr>
</thead>
<tbody>
{{#each invoice.services}}
<tr>
<td>
<strong>{{this.description}}</strong>
<br /><span>Date: {{this.date}}</span>
</td>
<td class="right">{{format(this.hours, '0.00')}}</td>
<td class="right">${{{format(this.rate, '0.00')}}/hr</td>
<td class="right">${{format(calc(this.hours, '*', this.rate), '0.00')}}</td>
</tr>
{{/each}}
</tbody>
</table>
Tips & Tricks
Professional Touches
- Logo Optimization:
<img src="" class="company-logo" alt="" style="max-height: 50pt; max-width: 200pt;" /> - Status Badge:
{{#if invoice.isPaid}}
<div style="position: absolute; top: 100pt; right: 50pt;
transform: rotate(-15deg); border: 4pt solid #10b981;
color: #10b981; font-size: 24pt; font-weight: bold;
padding: 10pt 20pt;">
PAID
</div>
{{/if}}
- QR Code for Payment: ```html
Scan to pay
4. **Terms and Conditions:**
```html
<div style="font-size: 8pt; color: #666; margin-top: 30pt;
border-top: 1pt solid #e5e7eb; padding-top: 10pt;">
<p><strong>Terms & Conditions:</strong></p>
<p></p>
</div>
Try It Yourself
Exercise 1: Customize the Design
Modify the invoice template:
- Change the color scheme (replace #2563eb)
- Add your company logo
- Adjust fonts and spacing
- Add custom footer information
Exercise 2: Add Calculations
Enhance the template with:
- Line item discounts
- Multiple tax rates
- Currency conversion
- Running totals
Exercise 3: Create Variations
Build specialized invoices for:
- Recurring subscriptions
- Project milestones
- Retail sales
- International customers (multi-currency)
Common Pitfalls
❌ Hardcoded Calculations
<!-- Hardcoded total - won't update -->
<td>Total: $1,000.00</td>
✅ Solution:
<!-- Dynamic calculation -->
<td>Total: ${{format(invoice.total, '0.00')}}</td>
❌ Missing Null Checks
<!-- Will break if customer.company is null -->
<p>{{customer.company}}</p>
✅ Solution:
<!-- Conditional rendering -->
{{#if customer.company}}
<p>{{customer.company}}</p>
{{/if}}
❌ Poor Number Formatting
<!-- No currency formatting -->
<td>${{invoice.total}}</td>
<!-- Output: $1234.5 -->
✅ Solution:
<!-- Proper formatting -->
<td>${{format(invoice.total, '0.00')}}</td>
<!-- Output: $1,234.50 -->
Customization Checklist
- Replace logo with your company logo
- Update color scheme to match brand
- Customize payment terms
- Add/remove optional sections
- Test with real customer data
- Verify calculations are correct
- Check all conditional sections
- Test with various item counts (1, 10, 50+ items)
Best Practices
- Always Calculate in C# - Don’t rely on template calculations for financial data
- Format Currency Consistently - Use format() for all monetary values
- Validate Data - Check for null/empty values before generation
- Test Edge Cases - Single item, many items, zero amounts
- Use Semantic HTML - Proper table structure for accessibility
- Include Metadata - Set proper PDF title and author
- Version Your Templates - Track template changes
- Keep Styling External - Use
Next Steps
- Business Letter - Multi-page correspondence
- Report Template - Complex reports with sections
- Data-Driven Report - Database integration
Continue learning → Business Letter