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.
tsawait 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.
tsconst 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.
tsimport { 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:
txt1. 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.