CSS page breaks for HTML to PDF
CSS page breaks for HTML to PDF: force breaks, keep tables and cards together, avoid orphan headings, and set page size with @page.
These rules work in any Chromium-based renderer. That includes Chrome's "Save as PDF", Puppeteer, Playwright, Gotenberg and html-pdf.com.
Quick answer. Add break-before: page where a new page should start and break-inside: avoid on things that must stay whole, then render:
curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" -H "Content-Type: application/json" \
-d '{"html": "<style>.pb{break-before:page}</style><h1>Page 1</h1><h1 class=\"pb\">Page 2</h1>"}' -o out.pdf
Force a new page
This rule starts an element on a fresh page.
.page-break { break-before: page; }
<section>Cover</section>
<section class="page-break">Chapter 1</section>
break-before: page is the modern property. The legacy page-break-before: always still works and is a safe fallback.
.page-break { page-break-before: always; break-before: page; }
Use break-after: page to break after an element instead.
Keep things together
These rules stop content from being split awkwardly across pages. To keep a card, a table row or a figure on one page, use break-inside: avoid.
.card, figure, tr, .totals { break-inside: avoid; page-break-inside: avoid; }
To keep a heading with the paragraph that follows it, avoid a break after the heading.
h2, h3 { break-after: avoid; page-break-after: avoid; }
To avoid lonely lines at the top or bottom of a page, set orphans and widows.
p { orphans: 3; widows: 3; }
Repeat table headers on every page
This makes long tables readable on every page. Chromium repeats <thead> automatically when a table spans pages, as long as you use real table markup.
<table>
<thead><tr><th>Item</th><th>Qty</th><th>Price</th></tr></thead>
<tbody>…hundreds of rows…</tbody>
</table>
thead { display: table-header-group; }
tr { break-inside: avoid; }
Page size and margins with @page
This section sets paper size and margins from CSS.
@page { size: A4; margin: 20mm 15mm; }
@page :first { margin-top: 40mm; }
Chromium only uses the CSS size when the renderer is told to prefer it. With html-pdf.com, set prefer_css_page_size.
curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" \
-d '{"html": "<style>@page{size:A5;margin:12mm}</style><h1>A5 booklet</h1>", "prefer_css_page_size": true}' -o a5.pdf
Or skip CSS and pass format (A4, Letter, …), landscape and margin in the request. Chromium also supports CSS @page margin boxes (Chrome 131+), which you can use for running headers and footers.
Common pitfalls
These are the most common reasons page breaks don't behave as expected.
- Breaks ignored inside flex or grid. Page-break properties apply to block-level boxes in normal flow. Wrap content in plain blocks (
display: block) where you need breaks. - Elements with
overflow: hiddenor fixed heights get clipped instead of flowing to the next page. Let print content grow (height: auto). - Backgrounds missing. Enable background printing (
print_background, on by default in html-pdf.com) and add-webkit-print-color-adjust: exact; print-color-adjust: exact;. - Screen styles leaking. Put print tweaks in
@media print { … }, because renderers use print media by default.
Test it in one call
You can check your page breaks in seconds. Paste your HTML into the converter on html-pdf.com or call the API.
You get 100 free pages, then pay from $0.005 per page, and purchased credits never expire. Next: headers, footers and page numbers.
FAQ
How do I force a page break when converting HTML to PDF?
Add break-before: page (or the legacy page-break-before: always) to the element that should start a new page. It works in Chrome, Puppeteer, Playwright, Gotenberg and html-pdf.com.
How do I stop a table row from splitting across pages?
Set tr { break-inside: avoid; }. Use real <thead> markup too, and Chromium repeats the header row on every page.
Why are my page breaks ignored?
Page-break properties only apply to block-level boxes in normal flow, so they're often ignored inside flex or grid containers. Fixed heights and overflow: hidden can also clip content instead of letting it flow onto the next page.
Does the API respect @page size from CSS?
Yes, when you set prefer_css_page_size: true in the request. Otherwise, pass format, landscape and margin as request options.
