DEV Community

Cover image for Dynamic Invoice PDF Generator Using IronPDF HTML Templates
Surinder Singh
Surinder Singh

Posted on

Dynamic Invoice PDF Generator Using IronPDF HTML Templates

Building a Dynamic HTML-to-PDF Invoice Engine in C# (IronPDF + Templates)

As part of a recent business application I was building, I ran into a very common but surprisingly hard problem: generating professional PDF invoices that can change layout per customer.

Some customers wanted a simple invoice. Others wanted branding, custom sections, different totals placement, extra notes, or even different terminology. Hardcoding invoice layouts in C# or using report designers quickly became unmaintainable.

So I built a dynamic HTML-to-PDF invoice engine in C#, where:

  • Each customer can have their own HTML invoice template
  • Templates support placeholders, loops, and conditional logic
  • Data comes from a strongly-typed Invoice model
  • PDFs are generated using IronPDF

In this article, I’ll walk you through the complete approach, architecture, and code so you can build something similar for your own .NET applications.


The Problem with Traditional Invoice Generation

Most invoice systems start simple:

  • One PDF layout
  • Hardcoded columns
  • Fixed structure

But real-world business software quickly demands:

  • Multiple invoice layouts
  • Customer-specific branding
  • Optional sections (discounts, VAT, notes, bank info)
  • Dynamic item rows
  • Localization (labels, dates, currencies)

Using report designers or PDF drawing APIs makes these changes expensive and fragile.

HTML, however, already solves layout, styling, and responsiveness extremely well.

So the real challenge becomes:

How do we safely and flexibly turn HTML templates into PDFs in C#?


High-Level Architecture

Here’s the architecture I ended up with:

Invoice Model (C#)
        ↓
HTML Template (with placeholders)
        ↓
Template Engine (replace, loops, conditions)
        ↓
IronPDF (HTML → PDF)
        ↓
Final Invoice PDF
Enter fullscreen mode Exit fullscreen mode

Each layer has a single responsibility, which keeps the system easy to extend and debug.


Designing the Invoice Model

Everything starts with a clean invoice model. This ensures templates stay readable and strongly typed.

public class Invoice
{
    public string InvoiceNumber { get; set; }
    public DateTime Date { get; set; }
    public DateTime DueDate { get; set; }

    public Party From { get; set; }
    public Party To { get; set; }

    public string ProjectName { get; set; }
    public string Notes { get; set; }

    public List<InvoiceItem> Items { get; set; }

    public decimal SubTotal { get; set; }
    public decimal Vat { get; set; }
    public decimal Discount { get; set; }
    public decimal Total { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

This model is what ultimately drives the HTML template.


HTML as the Invoice Layout

Instead of designing invoices in code, each invoice layout is just an HTML file.

Example snippet from an invoice template:

<div class="invoice-title">{{label:Invoice}}</div>
<div class="invoice-meta">
  {{label:Number}}: {{InvoiceNumber}}<br>
  {{Date}}
</div>
Enter fullscreen mode Exit fullscreen mode

This gives non-developers (or frontend developers) full control over layout, CSS, spacing, and branding.


Custom Placeholders Explained

The template engine supports several placeholder types.

1️⃣ Simple value placeholders

{{InvoiceNumber}}
{{DueDate}}
{{Total}}
Enter fullscreen mode Exit fullscreen mode

These map directly to properties on the invoice model.


2️⃣ Nested object placeholders

<strong>{{From.Name}}</strong><br>
{{From.AddressLine1}}
Enter fullscreen mode Exit fullscreen mode

This allows clean access to nested objects like From and To.


3️⃣ Loop placeholders (invoice items)

{{#Items}}
<tr>
  <td>{{Description}}</td>
  <td style="text-align:right">{{Price}}</td>
</tr>
{{/Items}}
Enter fullscreen mode Exit fullscreen mode

The engine repeats the block once per invoice item.


4️⃣ Label placeholders (localization-ready)

{{label:Invoice}}
{{label:DueDate}}
{{label:Total}}
Enter fullscreen mode Exit fullscreen mode

Labels can be resolved from a dictionary, making the same template usable across languages.


Executing the HTML Template

The template engine is intentionally simple:

  1. Load HTML file
  2. Replace scalar placeholders
  3. Process loops
  4. Apply conditional logic
string template = File.ReadAllText(htmlPath);
var htmlContent = htmlTemplateEngine.Execute(template, invoice);
Enter fullscreen mode Exit fullscreen mode

The result is pure HTML, ready for PDF rendering.


Converting HTML to PDF with IronPDF

This is where IronPDF shines.

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print;
renderer.RenderingOptions.PrintHtmlBackgrounds = true;
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;

var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs(savePath);
Enter fullscreen mode Exit fullscreen mode

IronPDF uses a Chromium-based engine, which means:

  • Modern CSS works
  • Page breaks behave predictably
  • Fonts and images render correctly

This makes it ideal for invoice documents.


Handling Page Breaks and Long Invoices

One issue I encountered was content being cut off vertically when invoices became long.

The fix was mostly CSS-based:

table {
  page-break-inside: auto;
}

tr {
  page-break-inside: avoid;
}

.invoice-page {
  min-height: 100%;
}
Enter fullscreen mode Exit fullscreen mode

IronPDF respects print CSS rules very well, so layout issues can usually be solved without code changes.


Why This Approach Scales Well

This system works well because:

  • New invoice layouts = new HTML file
  • No recompilation needed for layout changes
  • Designers and developers can work independently
  • Same engine supports invoices, quotations, statements

It also fits naturally into SaaS and multi-tenant systems.


GitHub Repository

The full working project is available here:

👉 https://github.com/yourusername/your-repo-name

You can clone it, modify templates, and adapt it to your own business needs.


Final Thoughts

HTML-to-PDF is one of those problems that looks easy at first and becomes complex very quickly.

By combining:

  • Clean C# models
  • Flexible HTML templates
  • A lightweight template engine
  • A robust PDF renderer like IronPDF

…I was able to build an invoice system that is flexible, maintainable, and production-ready.

If you’re building business software in .NET and struggling with document generation, this approach is well worth considering.


Resources


Thanks for reading — feel free to fork the repo or adapt the idea for your own projects.


Tags: #csharp #dotnet #pdf #html #opensource

Top comments (0)