Skip to content

PDF like on screen: settings that just work

If you want a PDF that looks as close as possible to what you see in the browser, this page gives you a ready-to-use request body that works in most cases. Copy it, replace the URL and the ready selector, add your API key, and adjust it only if needed.

A request body that covers the common cases

  • Loads the page fully: waits until the network is idle (networkidle0).
  • Waits for your content: uses optional waitForSelector support so SPAs and async content are ready.
  • Prints backgrounds: printBackground: true ensures CSS backgrounds and colors appear in the PDF.
  • Matches screen rendering: emulateMediaType: "screen" makes the PDF follow the on-screen layout instead of print CSS.

Use this with the PDF sync or PDF direct route.

json
{
  "page": {
    "goto": {
      "url": "https://your-page.com/your-document",
      "options": {
        "waitUntil": ["networkidle0"]
      }
    },
    "emulateMediaType": "screen",
    "waitForSelector": {
      "selector": "[data-render-ready]",
      "options": { "timeout": 15000, "visible": true }
    },
    "pdf": {
      "printBackground": true,
      "format": "A4"
    }
  }
}
  • waitUntil: ["networkidle0"] — Doppio waits until there are no more than 0 network connections for 500ms. Your images, fonts and API calls have time to finish.
  • emulateMediaType: "screen" — The page is rendered with @media screen (and no @media print), so the PDF looks like the browser view.
  • waitForSelector — Waits for a visible ready marker (up to 15s). Replace "[data-render-ready]" with a selector that appears only when the content is ready (e.g. "#invoice-ready", ".report-ready").
  • printBackground: true — Backgrounds and box-shadows are included in the PDF.

When to tweak

SituationWhat to do
Content appears after JS (SPA, dashboard)Set waitForSelector.selector to an element that exists only when the main content is rendered (e.g. "#invoice-ready", "[data-render-ready]"). Avoid generic selectors like "body" that can be visible before your data is ready.
Page sets a “ready” flagUse waitForFunction, e.g. "pageFunction": "window.__APP_READY__ === true". If you also set waitForSelector, Doppio runs waitForFunction first.
You want print styles (margins, page breaks)Use "emulateMediaType": "print" so the browser uses @media print CSS.
Very long page / lazy-loaded sectionsKeep networkidle0; if content loads asynchronously, expose a “loading complete” marker and target it with waitForSelector (or use waitForFunction). Doppio does not scroll the page before waiting.

Minimal “no surprises” example

If you don’t need to wait for a specific selector, this is the smallest body that still loads the page fully and prints like the screen:

json
{
  "page": {
    "goto": {
      "url": "https://your-page.com/your-document",
      "options": { "waitUntil": ["networkidle0"] }
    },
    "emulateMediaType": "screen",
    "pdf": { "printBackground": true }
  }
}

For more options (margins, paper size, timeout, etc.) see Common params and PDF params.

Dark theme / background

Sites that use a class on html for dark mode (e.g. html.dark) or prefers-color-scheme often render in light mode in headless Chrome, because no theme is forced. So the PDF can have a white or light background even with printBackground: true. That’s the page’s behavior in that context, not an API bug. To get a dark PDF, the page would need to force dark mode (e.g. always add the class, or detect the render context).

All rights reserved