Template-driven documents vs custom HTML: when to use each

A practical guide to choosing between template-driven document generation and raw HTML rendering, with real trade-offs around maintenance, flexibility, and team workflow.

pdftemplateshtmldocument-generationdeveloper-tools

Template-driven documents vs custom HTML: when to use each

Every team that generates PDFs eventually hits the same fork in the road: do you build documents from reusable templates, or do you render arbitrary HTML you control end-to-end? Both approaches work. Both have real costs. The wrong choice will haunt you six months in, when a designer wants to tweak a receipt and you're regex-ing your way through a 2,000-line React component.

This post breaks down when each approach makes sense, with concrete examples.

What each approach actually means

Template-driven means you define a document shape once — an invoice, a contract, a shipping label — with placeholders for dynamic data. At render time, you pass in a JSON payload and get back a PDF.

ts
await kamy.documents.render({ template: "invoice", data: { invoiceNumber: "INV-2041", customer: { name: "Acme Corp", email: "[email protected]" }, lineItems: [ { description: "API usage", quantity: 1, amount: 249.0 }, ], }, });

Custom HTML means you (or a rendering engine) produce the full HTML/CSS of the document at runtime, and pass that string to a PDF engine.

ts
const html = ` <html> <body> <h1>Invoice INV-2041</h1> <p>Total: $249.00</p> </body> </html> `; await kamy.documents.renderHtml({ html });

Same output. Very different workflows.

When template-driven wins

1. The document shape is stable

Invoices, receipts, order confirmations, statements, tickets — these have a fixed structure. The fields change; the layout doesn't. Templates encode that structure once and let you forget about it.

2. Non-engineers need to edit content

If your finance team wants to update the footer text on invoices, you don't want them opening a pull request against your Next.js repo. With a template system, the layout lives outside your codebase. Kamy templates, for example, are versioned separately so a copy change doesn't require a deploy.

3. You generate high volume of similar docs

When you're rendering 50,000 receipts a day, template caching, precompiled layouts, and predictable data shapes matter. Passing a JSON payload is faster on the wire than shipping full HTML per request.

ts
// Fast: only the data crosses the network await kamy.documents.render({ template: "receipt", data: orderPayload, });

4. You need consistency across a team

Twelve developers writing HTML by hand will produce twelve slightly different invoices. Templates enforce a single source of truth.

5. Audit and versioning matter

Regulated documents — contracts, financial statements, medical forms — often need to be reproducible months or years later. A versioned template plus stored input data gives you that. Reproducing "the exact HTML that one microservice generated in March" is much harder.

When custom HTML wins

1. Every document is different

Reports, dashboards exports, generated marketing collateral, one-off customer documents. If the structure itself changes per render, a template becomes a mess of conditionals. Just build the HTML.

2. You already have a rendering pipeline

If you have a React or server-side templating setup that produces the exact UI you want, and you just need it as a PDF, don't reinvent it. Render your existing components to HTML and pass that string to the PDF engine.

ts
import { renderToStaticMarkup } from "react-dom/server"; import { ReportPage } from "./ReportPage"; const html = renderToStaticMarkup(<ReportPage data={report} />); await kamy.documents.renderHtml({ html });

3. You need complex, data-driven layouts

Charts positioned relative to text, dynamic tables that flow across pages, conditional sections based on runtime logic — these are painful to express in a declarative template DSL. HTML + CSS gives you the full expressive power.

4. Prototyping or low volume

If you're rendering ten PDFs a day for an internal tool, the overhead of designing and deploying a template isn't worth it. Ship the HTML.

A hybrid approach

You don't have to pick one. Most mature systems use both:

  • Templates for high-volume, structured documents: invoices, receipts, statements.
  • Custom HTML for reports, exports, and one-off outputs.

A reasonable rule: if you'd have to write the same HTML more than three times with different data, it should be a template.

Practical checklist

Ask these questions before starting:

txt
1. Will the document structure change across renders? - No → template - Yes → custom HTML 2. Who updates the layout — engineers only, or others? - Engineers only → either works - Others → template 3. What's the volume? - < 100/day → custom HTML is fine - > 1,000/day → template (faster, cacheable) 4. Do you need reproducibility years later? - Yes → template + stored input - No → either works 5. Do you already have a component-based UI that matches the doc? - Yes → custom HTML, reuse it - No → template

The bottom line

Templates are for documents you'll render thousands of times with the same shape. Custom HTML is for documents where the layout itself is the output. Pick based on how the document changes, not on which technology feels more familiar. Both have their place, and any decent PDF API — Kamy included — supports both modes so you can mix them as your product grows.

Start with whichever fits your first real use case. Migrate the other direction only when the pain shows up.