ScreenshotNeo

BlogHTML to image & PDF

Convert HTML to PDF with CSS grid layouts intact

Learn how to choose and configure an HTML-to-PDF renderer for CSS Grid, with runnable Playwright code, print CSS guidance, troubleshooting, and validation steps.

By the ScreenshotNeo team4 October 20269 min read

To convert HTML to PDF with CSS grid layouts intact, choose a renderer that supports the Grid features your page uses, set print or screen media deliberately, and inspect PDFs made from representative pages. CSS Grid support is not a single pass/fail property: a renderer may support basic tracks and areas while lacking subgrid, certain intrinsic sizing behavior, or some fragmentation cases.

For a browser-based workflow, Playwright’s page.pdf() is a practical starting point when you need Chromium rendering and JavaScript. It renders with print CSS by default. If the intended output should match screen styling, emulate screen media before calling page.pdf(). WeasyPrint documents a useful subset of Grid for simple cases, with specific limitations. Prince is another HTML/XML-to-PDF candidate, but verify its current Grid support for your layout before choosing it.

Sources: Playwright Page API, WeasyPrint stable API reference, and Prince documentation. These sources document capabilities; they do not establish a comparative fidelity or performance ranking.

1. Check the Grid features your page depends on

Before changing renderers, inventory the layout. Note the Grid constructs, breakpoints, long-content behavior, images in grid items, and any page-break requirements. Then compare that list against the renderer’s current documentation and test the difficult combinations in the output PDF.

Layout requirement What to check
Track sizing and placement Explicit rows and columns, named lines or areas, fr units, minmax(), and repeat().
Automatic placement Auto rows or columns, dense flow, and whether the layout relies on column auto-flow.
Responsive layout Breakpoints and whether the PDF should use print styles or screen styles.
Content-driven sizing Long text, images, intrinsic sizes, min/max constraints, and items wider or taller than their tracks.
Pagination Whether rows split across pages, content moves cleanly to a new page, and repeated sections remain coherent.

WeasyPrint’s documented Grid subset

WeasyPrint’s stable API reference says its CSS Grid Layout Module Level 2 implementation “works for simple cases, but has some limitations.” It documents support for display: grid; grid-auto-*, grid-template-*, and other grid-* properties and shorthands; flexible fr lengths; line names and grid areas; auto rows and columns; z-index; repeat(X, *); minmax(); alignment; gaps; dense auto flow; ordering; box styling on grid containers and items; and fragmentation between rows.

The same reference lists these features as unsupported or untested: display: inline-grid; auto content size for grid containers; grid-auto-flow: column; subgrids; repeat(auto-fill, *) and repeat(auto-fit, *); auto margins on grid items; span with line names or flexible tracks; safe or unsafe alignment; baseline alignment; intrinsic-size grid items such as images; distribution beyond limits; grid items larger than their containers; min/max width and height on grid items; complex min-content/max-content cases; absolutely positioned and floating grid items; and fragmentation within rows. Treat each relevant item as a test case, not as a prediction that every other combination works.

2. Make the print stylesheet intentional

A browser preview commonly shows screen media. Playwright’s page.pdf() uses print media by default, so print styles, media queries, page dimensions, and background settings can change the result independently of Grid support.

@media print {
  /* Define the layout you want in the PDF. */
  .cards {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12mm;
  }

  /* Keep a card together when possible. */
  .card {
    break-inside: avoid;
  }
}

@page {
  size: A4;
  margin: 12mm;
}

Use print CSS when the PDF is a document with paper-oriented spacing and pagination. Use screen media when the PDF should resemble the screen composition. Set paper size, margins, and background printing explicitly; CSS feature support alone does not determine those output choices.

3. Generate the PDF with Playwright

The following Node.js example loads a local HTML file, waits for fonts and images to settle, then writes a PDF. Install the browser package and its Chromium browser as described in the Playwright installation guide. Save the script as make-pdf.mjs and run it with Node.js.

import { chromium } from 'playwright';
import path from 'node:path';
import { pathToFileURL } from 'node:url';

const inputPath = path.resolve('report.html');
const outputPath = path.resolve('report.pdf');
const browser = await chromium.launch({ headless: true });

try {
  const page = await browser.newPage();
  await page.goto(pathToFileURL(inputPath).href, { waitUntil: 'networkidle' });

  // Use print CSS by default. For screen styling instead, uncomment:
  // await page.emulateMedia({ media: 'screen' });

  await page.evaluate(async () => {
    await document.fonts.ready;
    const images = Array.from(document.images);
    await Promise.all(images.map((image) => {
      if (image.complete) return Promise.resolve();
      return new Promise((resolve) => {
        image.addEventListener('load', resolve, { once: true });
        image.addEventListener('error', resolve, { once: true });
      });
    }));
  });

  await page.pdf({
    path: outputPath,
    format: 'A4',
    printBackground: true,
    preferCSSPageSize: true,
    margin: { top: '12mm', right: '12mm', bottom: '12mm', left: '12mm' }
  });
} finally {
  await browser.close();
}

The example uses networkidle as a convenient starting condition, but pages with long polling or persistent network activity may never reach it. In that case, wait for a meaningful selector or an application-specific ready signal, with a bounded timeout. Ensure the local HTML’s linked assets are reachable by the browser process.

Relevant Playwright PDF options

Option Use
format Choose a named paper format such as A4 or Letter. The documented default is Letter.
width, height Set custom page dimensions when a named format is not appropriate.
margin Set top, right, bottom, and left margins explicitly.
printBackground Include background colors and images. The documented default is false.
scale Scale rendered content to fit the page when needed; check the API for the installed version’s range and behavior.
pageRanges Limit output to selected pages when the document is large or only a subset is needed.
preferCSSPageSize Let CSS @page size take priority over format, width, or height. The documented default is false.
Tagged output Use the documented tagged-PDF option when relevant to your output requirements, then validate the resulting document for the needs of your workflow.

Playwright’s documented defaults include Letter paper, background printing off, and preferCSSPageSize off. Defaults and available options can change by version, so consult the API reference for the installed version.

4. Choose a renderer for the job

Renderer Good fit to investigate Grid and output checks
Playwright Pages that need browser rendering, JavaScript, and configurable PDF output. page.pdf() uses print media by default. Set media mode and PDF options deliberately. Test the actual Chromium version and content.
WeasyPrint HTML/CSS documents where its documented simple-case Grid support matches the page. Use its published supported and unsupported/untested lists as a feature checklist. Also check fonts, links, bookmarks, attachments, and forms if those matter.
Prince HTML/XML-to-PDF workflows requiring CSS and JavaScript and print-focused PDF controls. The sources here describe the product and reference controls, but do not establish a Grid support matrix. Verify the exact features in current vendor documentation and with representative PDFs.

Compare the constructs your page uses: explicit tracks, named areas, auto placement, subgrid, intrinsic sizing, and fragmentation. Also compare media behavior, paper controls, JavaScript requirements, and output requirements such as tags or print-production controls. Available sources do not establish a controlled speed, price, or fidelity comparison, so decide using your own requirements and validation runs.

5. Validate the PDF, not just the HTML preview

  1. Prepare representative pages, including the most complicated Grid, longest text, largest images, and narrowest or widest responsive states.
  2. Choose print or screen media and make the choice explicit in CSS or the rendering call.
  3. Set paper size, margins, and background behavior explicitly.
  4. Generate PDFs in the intended deployment environment and renderer version.
  5. Inspect page boundaries, clipped content, row fragmentation, font glyphs, image sizing, and alignment at the actual paper size.
  6. Repeat after changing the renderer version, CSS, fonts, or asset loading behavior.
HTML + CSS → renderer settings → PDF pages → visual inspection
Validate the generated pages at their final paper size, including page breaks and content-driven Grid sizing.

6. Troubleshoot CSS Grid not working in PDF

Symptom Likely cause Fix
The PDF layout differs from the browser preview. The renderer uses print media or print-specific styles. Check @media print and page rules. In Playwright, emulate screen media before PDF generation only if screen styling is the intended output.
Columns collapse or distribute unexpectedly. The layout uses a Grid feature the renderer does not support, has unusual intrinsic sizing, or lacks available page width. Check the renderer’s current feature notes; simplify or replace the unsupported construct and retest at the target paper size.
Auto-fit or auto-fill tracks do not behave as expected. These are listed as unsupported or untested in WeasyPrint’s documented Grid notes. For WeasyPrint, try explicit track counts for the PDF stylesheet and verify the result. For other renderers, check their current support and test it.
Subgrid layout changes or disappears. Subgrid is listed as unsupported or untested in WeasyPrint’s Grid notes. Use explicit tracks in a PDF-specific stylesheet or select and validate a renderer that supports the required subgrid behavior.
Images or text overflow a grid item. Intrinsic sizing, long unbreakable content, or oversized items can affect track sizing; several such cases are explicitly limited or untested in WeasyPrint. Constrain media with max-width: 100%, permit long text to wrap, define track minimums, and test the renderer’s sizing behavior.
Background colors or images are missing. Playwright’s documented printBackground default is false. Set printBackground: true when backgrounds belong in the PDF.
The page size ignores @page. Playwright’s preferCSSPageSize default is false. Set it to true when CSS page size should control output, or remove conflicting dimensions and set the API option you intend to take precedence.
Fonts show missing-character boxes. WeasyPrint’s font documentation notes that missing glyphs can trigger a warning and render a .notdef glyph; the font may not contain the needed character or may not be available to the renderer. Make the required font available to the rendering process, include glyph coverage for the content, and inspect renderer warnings.
PDF generation hangs waiting for page readiness. networkidle may not occur on pages with persistent requests. Wait for a specific ready selector or application signal with a timeout, then handle missing or failed assets explicitly.
Rows split awkwardly across pages. Grid fragmentation support varies; WeasyPrint documents fragmentation between rows but lists fragmentation in rows as unsupported or untested. Test long rows and page breaks. Restructure content or apply print break rules where appropriate, then inspect every resulting page.

7. Reliability, performance, and cost

Rendering cost and speed depend on the renderer, document complexity, assets, and deployment environment; the cited documentation does not provide a controlled comparison. For reliable output, pin the renderer and browser version used by your application, bound readiness waits, make external assets available, and keep representative PDFs for review after changes. Fonts and images should be loaded before capture. Very long documents can take longer to render and produce larger files; limiting page ranges or simplifying unnecessary assets may help when those tradeoffs fit the use case.

For Playwright, a browser must be installed in the runtime, and the deployment environment must permit launching it. WeasyPrint and Prince have their own runtime and licensing considerations; consult their current official documentation for environment and commercial terms. No universal cost or throughput conclusion follows from the sources cited here.

Or skip the browser setup

If you need a screenshot or PDF capture from a URL without wiring up a browser renderer, ScreenshotNeo is a website screenshot API and MCP server. It returns PNG, JPEG, WebP, or PDF from one GET request. Its PDF options include paper size, margins, landscape mode, and page ranges; see the ScreenshotNeo API documentation for parameters and current usage.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.pdf
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com", "format": "pdf"},
    timeout=90,
)
r.raise_for_status()
open("shot.pdf", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com',
  format: 'pdf'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo request failed: ${res.status}`);
await Bun.write('shot.pdf', res);

ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. For CSS Grid fidelity, validate the returned PDF against your page just as you would with any renderer.

Sign up free for 1,000 screenshots a month, no card required.

FAQ

Does using CSS Grid guarantee the same PDF layout as the browser?

No. The renderer, media mode, paper dimensions, and support for the specific Grid constructs all affect the result. Inspect output from the intended renderer.

Should I use print CSS or screen CSS?

Use print CSS for a paper-oriented document and screen CSS when matching the on-screen composition is the goal. In Playwright, PDF generation defaults to print media.

Can I use Prince for a Grid-heavy PDF?

It is a documented HTML/XML-to-PDF option with CSS and JavaScript support, but the cited sources do not establish its exact Grid feature coverage. Check current Prince documentation and test the required layout.

Is a successful PDF generation call proof that the layout is correct?

No. A generated file can still have clipping, unexpected pagination, missing glyphs, or altered track sizing. Review representative pages at final dimensions.