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