Billing and receivables / A4
HTML invoice template with CSS
The standard commercial invoice: a demand for payment with line items, a tax figure, and terms that say when the money is due.
When this document is the right one
An order completes or a billing period closes and a customer has to be told what to pay and by when. This is the document a finance team files and an auditor asks for years later, which is why it becomes a PDF rather than staying a page that re-renders from live data.
Why each part is there
The fields are visible in the markup. What is not visible is why they are present, which is the part that gets removed first when someone adapts a template for their own use.
- A number that is sequential and never reused, because that is what a payment, a credit note and an audit are all traced by.
- Two dates, not one. The issue date starts the retention clock and the due date is what a collections process counts from.
- Legal entity names for both parties rather than trading names, because a trading name is not who the debt is owed by.
- Per-line quantity, unit price and line total, so a dispute can be about one line rather than about the whole invoice.
- Tax broken out, then the gross total, in that order. A single tax-inclusive figure cannot be reconciled against a tax return.
- Payment instructions with the reference to quote, which is the single field that decides whether the payment is allocated automatically or by hand.
The template
A complete file. Doctype, stylesheet, body: paste it into a renderer unchanged and it produces a finished A4 page. Replace the sample data and the styling holds.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Invoice INV-2026-0184</title>
<style>
@page { size: A4; margin: 16mm 15mm 20mm; }
* { box-sizing: border-box; }
body { margin: 0; font: 10pt/1.45 "Helvetica Neue", Arial, sans-serif; color: #111; }
header { display: flex; justify-content: space-between; align-items: flex-start;
border-bottom: 2px solid #111; padding-bottom: 10mm; }
.brand { font-size: 15pt; font-weight: 700; letter-spacing: -0.01em; }
.doc-type { text-align: right; }
.doc-type h1 { margin: 0; font-size: 20pt; letter-spacing: 0.04em; text-transform: uppercase; }
.doc-type dl { margin: 4mm 0 0; display: grid; grid-template-columns: auto auto;
gap: 1mm 4mm; font-size: 9pt; }
.doc-type dt { color: #666; text-align: right; }
.doc-type dd { margin: 0; font-variant-numeric: tabular-nums; }
.parties { display: grid; grid-template-columns: 1fr 1fr; gap: 10mm; margin-top: 9mm; }
.parties h2 { margin: 0 0 2mm; font-size: 8pt; text-transform: uppercase;
letter-spacing: 0.08em; color: #666; }
address { font-style: normal; }
table { width: 100%; border-collapse: collapse; margin-top: 9mm; }
/* Repeats the column headers on every page the table spills onto. */
thead { display: table-header-group; }
th { font-size: 8pt; text-transform: uppercase; letter-spacing: 0.06em; color: #666;
text-align: left; border-bottom: 1px solid #111; padding: 0 0 2mm; }
td { padding: 2.5mm 0; border-bottom: 1px solid #e4e4e4; vertical-align: top; }
tr { break-inside: avoid; }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.desc small { display: block; color: #666; font-size: 8.5pt; }
/* The totals must never be orphaned from the table on their own page. */
.totals { margin-top: 6mm; margin-left: auto; width: 78mm; break-inside: avoid; }
.totals div { display: flex; justify-content: space-between; padding: 1.5mm 0; }
.totals .grand { border-top: 2px solid #111; margin-top: 2mm; padding-top: 2.5mm;
font-size: 12pt; font-weight: 700; }
.totals span:last-child { font-variant-numeric: tabular-nums; }
.terms { margin-top: 12mm; break-inside: avoid; border-top: 1px solid #e4e4e4;
padding-top: 5mm; font-size: 9pt; }
.terms strong { display: block; margin-bottom: 1.5mm; }
</style>
</head>
<body>
<header>
<div>
<div class="brand">Northgate Supply Co.</div>
<address>
18 Wharf Road<br>Bristol BS1 4RN<br>United Kingdom<br>
VAT GB 412 8871 03
</address>
</div>
<div class="doc-type">
<h1>Invoice</h1>
<dl>
<dt>Number</dt><dd>INV-2026-0184</dd>
<dt>Issued</dt><dd>14 May 2026</dd>
<dt>Due</dt><dd>13 June 2026</dd>
</dl>
</div>
</header>
<section class="parties">
<div>
<h2>Billed to</h2>
<address>
<strong>Calder Interiors Ltd</strong><br>
Attn: Accounts Payable<br>
7 Meridian Court, Leeds LS11 5AL<br>
United Kingdom
</address>
</div>
<div>
<h2>Reference</h2>
<p style="margin:0">Purchase order PO-88213<br>Contract NG-2025-11</p>
</div>
</section>
<table>
<thead>
<tr>
<th>Description</th>
<th class="num">Qty</th>
<th class="num">Unit price</th>
<th class="num">Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td class="desc">Oak veneer panel, 2400 x 1200
<small>Batch NG-4471, delivered 02 May</small></td>
<td class="num">24</td>
<td class="num">86.00</td>
<td class="num">2,064.00</td>
</tr>
<tr>
<td class="desc">Edge banding, 22mm, 50m roll</td>
<td class="num">6</td>
<td class="num">41.50</td>
<td class="num">249.00</td>
</tr>
<tr>
<td class="desc">Delivery, palletised
<small>Two-day service, Leeds</small></td>
<td class="num">1</td>
<td class="num">115.00</td>
<td class="num">115.00</td>
</tr>
</tbody>
</table>
<div class="totals">
<div><span>Subtotal</span><span>2,428.00</span></div>
<div><span>VAT at 20%</span><span>485.60</span></div>
<div class="grand"><span>Total due</span><span>GBP 2,913.60</span></div>
</div>
<section class="terms">
<strong>Payment terms</strong>
Net 30. Payment by bank transfer to Northgate Supply Co., sort code 20-14-88,
account 41338207. Quote the invoice number as the payment reference.
Late payment interest is charged at 8% above base rate under the
Late Payment of Commercial Debts (Interest) Act 1998.
</section>
</body>
</html>The layout decision worth understanding
The table header repeats on every page with display: table-header-group, which matters because line items are unbounded and a second page of numbers with no column headings is unreadable. The totals block carries break-inside: avoid so it can never be split across a page boundary, and it sits after the table rather than in a fixed footer so it flows with the content.
The mistake people make adapting it
Letting the totals block land alone on a second page. It is technically correct and it looks like a mistake, and it invites the reader to check whether a page is missing. If the table nearly fills the page, the honest fix is to reduce the line height or widen the page, not to hope.
Rendering it
The page box is declared in the template's own CSS as A4, so the size travels with the markup rather than living in the code that calls the renderer. A4 is also one of the six named formats the API accepts, so it can be passed in the request options instead if that suits your setup better.
curl -X POST https://api.pdfpipe.xyz/v1/pdf \
-H "Authorization: Bearer $PDFPIPE_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "<!doctype html>...",
"options": { "format": "A4" }
}' \
--output invoice.pdfFrequently asked
Can I use this template commercially?
Yes. Copy it, change it, ship it. It is sample markup written to be taken, and the sample data in it is invented, so replace the names and the numbers before anyone sees it.
Why is the page size in the CSS rather than in the API call?
Because a invoice is a A4 document, and that is a property of the document rather than of the code that renders it. Declaring it in the page box means the size cannot be lost between the template and the call, which is the usual way a document ends up on the wrong paper.
Will it look the same in every renderer?
The layout will, because it uses ordinary CSS. The page break behaviour will not: break-inside and break-after are implemented differently by browser engines and by dedicated print engines, and some of the paged media specification is not implemented in browser renders at all. That difference is documented per property in the paged CSS reference.
Other templates
Documents from the same part of a business, and one from each of the other groups.
EU VAT invoice HTML template
A cross-border invoice inside the EU, with both parties' VAT identification numbers, tax broken out per rate, and a reverse charge statement.
GST tax invoice HTML template for India
An Indian tax invoice with GSTIN for both parties, HSN codes per line, and tax split into CGST and SGST or charged as IGST depending on the place of supply.
Commercial invoice template for customs
The customs document that travels with an international shipment: what the goods are, what they are worth, where they were made, and under what terms they move.
Proforma invoice HTML template
A document shaped like an invoice that is deliberately not one: it commits nobody to pay and exists so the buyer can raise an order, open a credit, or clear an import.
Payment receipt HTML template
Proof that a payment was made: the amount, the payment reference, the method, and what it was for.
Quotation HTML template with CSS
A priced proposal with a validity date, a scope, optional items kept out of the headline total, and somewhere to sign.
Packing slip HTML template
The document that goes in the box: what was ordered, what was actually shipped, and no prices at all.
Every template
The full set, grouped by what part of a business produces the document.
A4 page size for PDF generation
A4 is the default page for business documents everywhere except North America, and the default this API uses when no format is given.
Paste the template into the playground and get the PDF back. No signup, no key.