Requirements
- Download report-template.html and report-data.json below. The data contains 60 fictional rows, including longer descriptions.
- API generation requires a Templatr key and available quota. You can view the sample PDF without an account.
1. Define the paper and content boundaries
@page selects A4 with 18 mm margins. Do not set a fixed page height on body: let content flow onto the next page. Templatr’s renderer respects CSS @page dimensions.
break-inside: avoid on tr asks the renderer to keep a row together. thead repeats the table heading. break-after: avoid discourages orphaned headings; break-before: page starts the appendix on a new page.
A row taller than a page cannot remain intact: shorten it or allow it to split. Do not apply break-inside: avoid to an entire table that needs multiple pages.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{report.title}}</title>
<style>
@page { size: A4; margin: 18mm; }
body { font: 11pt Arial, sans-serif; color: #172033; }
h1 { font-size: 24pt; }
h2 { break-after: avoid; }
table { width: 100%; border-collapse: collapse; }
thead { display: table-header-group; }
th, td { border-bottom: 1px solid #dbe2ea; padding: 3mm; text-align: left; }
tr, .summary { break-inside: avoid; }
td { overflow-wrap: anywhere; }
th:first-child, td:first-child { width: 22mm; white-space: nowrap; }
th:last-child, td:last-child { width: 25mm; white-space: nowrap; }
.appendix { break-before: page; }
</style>
</head>
<body>
<h1>{{report.title}}</h1>
<p>{{report.period}}</p>
<table>
<thead><tr><th>Reference</th><th>Description</th><th>Amount</th></tr></thead>
<tbody>{{ items loop }}<tr><td>{{item.reference}}</td><td>{{item.description}}</td><td>{{item.amount}}</td></tr>{{ end loop }}</tbody>
</table>
<div class="summary"><h2>Summary</h2><p>{{report.summary}}</p></div>
<section class="appendix"><h2>Appendix</h2><p>This section starts on a new page. All values are fictional.</p></section>
</body>
</html>
2. Use data that spans more than one page
The preview below shows two rows; the download contains 60, enough rows to check repeated headings and page boundaries. Change the descriptions and add a long unbroken word to check overflow-wrap. The JSON array order determines row order.
{
"report": {
"title": "Monthly activity report",
"period": "September 2026",
"summary": "60 demonstration rows. All amounts are display values calculated before rendering."
},
"items": [
{
"reference": "DEMO-001",
"description": "Integration review and document layout testing",
"amount": "EUR 25.00"
},
{
"reference": "DEMO-002",
"description": "Integration review and document layout testing",
"amount": "EUR 25.00"
}
]
}3. Generate the report
Set TEMPLATR_API_KEY, upload the template, then replace TEMPLATE_ID_FROM_RESPONSE with the returned template_id. curl sends the JSON file unchanged and saves the PDF response.
export TEMPLATR_API_KEY='YOUR_API_KEY'
curl --fail-with-body "https://api.templatr.app/upload" \
-H "Authorization: Bearer $TEMPLATR_API_KEY" \
-F "[email protected]" \
-F "name=Example report"
export TEMPLATR_TEMPLATE_ID='TEMPLATE_ID_FROM_RESPONSE'
curl --fail-with-body "https://api.templatr.app/pdf/$TEMPLATR_TEMPLATE_ID?format=pdf" \
-H "Authorization: Bearer $TEMPLATR_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @report-data.json --output report.pdfChecks and troubleshooting
- Check that DEMO-001 through DEMO-060 each appear once, in order.
- On intermediate pages, check the Reference, Description and Amount headings and make sure content does not overlap the margins.
- The appendix should start on its own page. Total page count depends on fonts and the length of your data.
- If a table is clipped horizontally, reduce column widths or unbreakable content. Keep background colors in the template CSS; the renderer has background printing enabled.
- Remote images and external fonts may be blocked. This example uses a system font with no scripts or network resources.