Why your Markdown-to-PDF keeps cutting code blocks in half (and how to fix it)
June 11, 2026
You export a README to PDF. Page 2 ends with the first six lines of a function. Page 3 starts with the other four. The opening brace is on one page, the closing brace on the next, and the diff you wanted to share now looks broken.
Every popular Markdown-to-PDF tool does some version of this. It’s not random — it’s a predictable consequence of how documents get turned into pages, and it’s fixable in about a dozen lines of CSS.
Two different bugs that look the same
When a code block, table, or image gets mangled in a PDF, it’s usually one of two failures.
The element gets split. The print engine treats your page as a fixed-height box and pours content into it. When it reaches the bottom edge, it cuts — wherever it happens to be. If that’s the middle of a <pre> block or row 14 of a 30-row table, too bad. The engine doesn’t know those elements are supposed to stay together unless you tell it.
The whole page is an image. A lot of browser-based converters reach for html2canvas or html2pdf.js. These take a screenshot of your rendered HTML, then paste that bitmap into a PDF. Now every problem compounds: text isn’t selectable or searchable, links are dead, fonts are fuzzy at print resolution, and the page break is a blind horizontal slice through a picture. You can always spot this output — try to select a line of text. If you can’t, it’s an image.
The fix for the first bug is CSS. The fix for the second is to stop rasterizing entirely.
Tell the engine what not to break
CSS has a property built for exactly this: break-inside. Set it to avoid on the elements that should never split, and the print engine moves the whole element to the next page instead of cutting it.
/* Keep atomic blocks whole across page boundaries */
pre,
table,
img,
figure,
blockquote {
break-inside: avoid;
page-break-inside: avoid; /* older engines */
}
/* Don't strand a heading at the bottom of a page */
h1, h2, h3, h4, h5, h6 {
break-after: avoid;
}
/* Repeat the header row when a table spans pages */
thead { display: table-header-group; }
tr { break-inside: avoid; }
/* Long code lines wrap instead of overflowing the page */
pre code { white-space: pre-wrap; word-break: break-word; }
/* Define the actual page */
@page { size: A4; margin: 18mm; }
A few things worth calling out:
thead { display: table-header-group }is the one most tools miss. It tells the engine to reprint the header row at the top of every page a long table continues onto, so page 2 of a table is still readable.break-after: avoidon headings stops the classic orphan where a section title sits alone at the bottom of a page and its content starts on the next one.white-space: pre-wrapon code matters because the alternative — a code block wider than the page — either overflows the margin or gets clipped. Wrapping keeps every character on the page.@pageis where you set real page size and margins. Without it you inherit whatever the browser guesses.
If you want a deliberate break, give yourself an escape hatch:
.page-break { break-before: page; }
Then drop <div class="page-break"></div> into your Markdown wherever you want a clean new page.
Keep it vector
The second half of the fix is choosing the right rendering path. The browser already has a high-quality, document-aware PDF engine built in — it’s what runs when you hit “Save as PDF” in the print dialog. Feed it real HTML and the CSS above, and it produces a true vector PDF: selectable text, embedded fonts, clickable links, and page breaks that respect break-inside.
html2canvas throws all of that away to take a screenshot. So the rule is simple: render to the print engine, not to a canvas. In practice that means building a clean HTML document, applying print CSS, and calling window.print() (or printing from a hidden iframe so you don’t disturb the page). The break-inside rules only work on this path anyway — they’re meaningless to a bitmap.
How to check any tool in ten seconds
Before you trust a converter with your docs, run two tests:
- Selectability. Open the exported PDF and try to highlight a sentence. Real text highlights. An image doesn’t.
- A long code block and a wide table. Paste in a 40-line function and a 30-row table, export, and look at the page boundaries. If either gets sliced through the middle, the tool isn’t setting
break-inside.
Most tools fail at least one of these. That’s the whole opportunity.
We built this in
Our Markdown-to-PDF converter applies exactly these rules — break-inside: avoid on code, tables, images and headings, repeating table headers, wrapping long lines, and a configurable @page — and renders through the browser’s vector print engine, not a canvas. It runs entirely client-side, so your file never leaves your browser, and it’s free with no login. Drop in the same long-code-block-and-wide-table test above and watch nothing get cut.