wkhtmltopdf page breaks not working: fixes that actually help
Broken page breaks are the single most-cited wkhtmltopdf complaint: tables
split mid-row, page-break-inside: avoid gets ignored, and the
PDF that looked fine on your laptop breaks differently on the server. The
root cause is the engine — a Qt WebKit snapshot from ~2015 with
incomplete CSS fragmentation — but these workarounds fix the common
cases without switching tools today.
1. Make layout deterministic first
Most “breaks move around randomly” reports are smart shrinking: wkhtmltopdf rescales the page to fit the paper, so break positions depend on rendered width and DPI — which differ between your dev machine and the server. Pin all of it:
# Predictable layout: stop rescaling, fix DPI, use print CSS, set the paper
wkhtmltopdf \
--disable-smart-shrinking \
--dpi 96 \
--zoom 1 \
--print-media-type \
--page-size A4 \
--margin-top 20mm --margin-bottom 20mm \
invoice.html invoice.pdf
2. The inline-block wrapper trick
Old WebKit honours page-break-inside: avoid inconsistently on
plain <div>s, but it never splits an
inline-block — it moves the whole box to the next page.
This is the most reliable keep-together primitive wkhtmltopdf has:
<!-- The inline-block wrapper trick: old WebKit moves inline-blocks to the
next page as a unit, even where it ignores 'avoid' on a plain div. -->
<div style="display: inline-block; width: 100%; page-break-inside: avoid;">
<h3>Line items</h3>
<p>This block will never be split across pages.</p>
</div>
3. Tables: repeat headers, keep rows whole
/* Tables: repeat the header, keep rows whole */
table { page-break-inside: auto; }
thead { display: table-header-group; } /* header repeats on every page */
tfoot { display: table-footer-group; }
tr { page-break-inside: avoid; page-break-after: auto; }
/* Manual break helper — force a break exactly where you want one */
.page-break {
page-break-before: always;
clear: both; /* floats near a break confuse old WebKit; clear them first */
}
Two extra table gotchas: with border-collapse: collapse, borders
that land exactly on a page boundary can disappear — if that bites you,
switch to border-collapse: separate; border-spacing: 0 and put
borders on cells. And rows taller than one page can never be kept whole;
break the content into multiple rows.
4. Avoid flexbox and floats near breaks
/* wkhtmltopdf's engine predates flexbox-in-print and grid entirely.
For anything that must survive a page break, use block or table layout. */
/* BAD in wkhtmltopdf — renders as a broken column or overlaps at breaks: */
.summary { display: flex; justify-content: space-between; }
/* GOOD — same visual result, fragmentation-safe in old WebKit: */
.summary { display: table; width: 100%; }
.summary > div { display: table-cell; }
5. Know when you’re at the engine’s limit
If you need break-after: avoid (keep headings with their
content), CSS grid, orphans/widows that behave, or
breaks inside flex layouts — wkhtmltopdf cannot do it and never will;
the project is archived. Modern
Chromium implements the full fragmentation spec: see our
complete guide to page-break CSS
for what becomes possible after you migrate.
FAQ
Why does wkhtmltopdf ignore page-break-inside: avoid?
wkhtmltopdf embeds a Qt WebKit engine frozen around 2015 with incomplete CSS fragmentation support. page-break-inside: avoid is honoured only on simple block-level elements — not reliably on table rows, floated content, or inline formatting contexts. The workarounds on this page reshape your markup into the cases the old engine handles.
What does --disable-smart-shrinking actually do?
"Smart shrinking" rescales the rendered page to fit the paper width, changing the effective pixel-to-millimetre ratio. That rescaling is why breaks land in different places on your laptop versus the server. Disabling it makes layout math predictable; pair it with --dpi 96 and explicit --page-size.
Will these fixes make wkhtmltopdf reliable?
They fix the most common failures, but the engine is archived and unmaintained — flexbox, grid and the modern break-* properties will never work. Treat these as a bridge while you migrate to a Chromium-based renderer, where page-break CSS works as specified.
…or skip all of this with one API call.
NippyPDF is a managed Chromium rendering core behind a single
POST /v1/pdf — no browser fleet, no page-break roulette,
around $0.003/document.
Try the live demo Join the waitlist Modern page-break CSS guide (Chromium) →