CSS page breaks for HTML to PDF
The print CSS that makes multi-page invoices and statements look right: page size and margins, table headers on every page, and rows and totals that never split.
By Saurav N, senior software engineer · Updated
· 5 min readSet the page with @page
@page sets the paper size and margins for every page. Chrome, and so Puppeteer, Playwright and Templater, all honour it. Keep the bottom margin large enough for a footer with page numbers.
Tables that span pages
thead { display: table-header-group } repeats the header row at the top of each page. tr { break-inside: avoid } stops a row from being cut in half. Both matter most for long line-item tables.
Keep things together, or force a new page
break-inside: avoid on the totals block keeps it in one piece. break-before: page starts a section, such as terms or an annexure, on a fresh page. break-after: avoid on headings stops them being stranded at the bottom of a page.
@page {
size: A4;
margin: 16mm 14mm 20mm;
}
/* Repeat the table header on every page, keep a row in one piece. */
thead { display: table-header-group; }
tr { break-inside: avoid; }
/* Keep the totals block with the last rows, and start annexures on a new page. */
.totals { break-inside: avoid; }
.annexure { break-before: page; }
/* Headings never sit alone at the bottom of a page. */
h2, h3 { break-after: avoid; }
/* Print the colours you designed. */
body { print-color-adjust: exact; -webkit-print-color-adjust: exact; }Page numbers
Chrome fills <span class="pageNumber"> and <span class="totalPages"> in a PDF footer. In a Templater PDF template, write {{pageNumber}} and {{totalPages}} in the footer and they become those spans.
Questions
Do the older page-break-* properties still work?
Yes. page-break-before, page-break-after and page-break-inside are aliases of the break-* properties, and Chrome supports both.
Why does break-inside: avoid not work on my div?
It applies to block-level boxes. Inside flex or grid containers Chrome often ignores it; wrap the part in a normal block element.
Why are my background colours missing?
Browsers skip backgrounds when printing unless print-color-adjust: exact is set, or printBackground is on in Puppeteer.