Ask Anvil

Answers to questions about automating PDFs, e-signatures, Webforms, and other paperwork problems.
PDFs
Categories

Why does my flexbox or CSS grid layout break when I convert HTML to PDF?

The symptom

The markup renders correctly in your browser. Push the same HTML through your PDF pipeline and a display: flex row stacks vertically, a three column grid collapses to one column, and gap spacing disappears. Nothing throws an error. You just get a document that looks wrong.

The cause is the rendering engine, not your CSS

Unsupported CSS declarations are not errors. A renderer that does not implement display: grid drops the declaration and lays the element out as a normal block, which is exactly the "everything stacked in one column" result. So the first question is not what is wrong with your stylesheet. It is which engine is actually drawing the page.

If your pipeline shells out to wkhtmltopdf, that is almost certainly what is happening. Its maintainer's own status page states that Qt 4, which wkhtmltopdf uses, has not been supported since 2015, and the WebKit inside it has not been updated since 2012. CSS Grid Layout Level 1 did not reach W3C Candidate Recommendation until 2016. The engine predates the layout model you are writing against, and the project itself was archived on January 2, 2023, so it is read only and will not catch up.

The fix: render with a current browser engine

Render the HTML with headless Chromium instead. The same engine that laid out your page in the browser lays it out for the PDF, so flex and grid behave the way you already tested.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });

const pdf = await page.pdf({
  format: 'Letter',
  printBackground: true,
  margin: { top: '0.5in', bottom: '0.5in', left: '0.5in', right: '0.5in' },
});

await browser.close();

The wkhtmltopdf status page itself points users at Puppeteer for exactly this reason, so this is not a fringe opinion about the tool. It is the maintainer's own recommendation.

Two things that still bite

First, page.pdf() renders with the print CSS media type, not screen. Rules inside a screen only media block are dropped and print rules apply, so a layout that exists only under @media screen will still collapse. Call page.emulateMediaType('screen') before page.pdf() if that is what you want. printBackground also defaults to false, so background colors and images are omitted unless you opt in.

Second, pin a recent Chromium in CI. Chromium's flex and grid fragmentation, which decides how a flex or grid container splits across a page break, shipped in Chrome 103, with fragmentation support for printing in Chrome 108. An older pinned build can lay the grid out correctly on page one and mangle it at the page boundary, which reads like a CSS bug but is a version problem.

Back to All Questions

The fastest way to build software for documents

Anvil Document SDK is a comprehensive toolbox for product teams launching document flows where PDF filling, signing, and complex conditional scenarios are necessary.
Explore Anvil
Anvil Webforms