How to Use Playwright to Capture Web Pages as PDFs with Background Colors
Set Playwright’s PDF background option, choose print or screen CSS, and control colors, paper size, margins, and page ranges.
To include page backgrounds in a Playwright PDF, set printBackground: true in JavaScript or print_background=True in Python. The option defaults to false. Playwright renders PDFs using print CSS by default; if you need the page’s screen styles, call page.emulateMedia({ media: 'screen' }) before generating the PDF. For color fidelity, add -webkit-print-color-adjust: exact to the page’s CSS as well. These controls do different things: the PDF option includes background graphics, while the CSS rule asks the browser to preserve print colors. Playwright Page.pdf() API
1. Minimal JavaScript example
This complete Node.js script opens a page, chooses screen media, and saves a PDF with backgrounds. Omit the emulateMedia line if the page’s print stylesheet is what you want.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'load' });
// Use screen styles instead of Playwright's default print media.
await page.emulateMedia({ media: 'screen' });
await page.pdf({
path: 'page.pdf',
format: 'A4',
printBackground: true,
});
} finally {
await browser.close();
}
})();
Install the package and browser once in the project environment, then run the script:
npm install playwright
npx playwright install chromium
node capture.js
page.pdf() returns a PDF buffer; supplying path writes that buffer to a file. Keep browser shutdown in a finally block so a navigation or PDF error does not leave the browser process running.
2. Backgrounds, color fidelity, and media
Include background graphics
printBackground is the switch that includes CSS background colors and images in the generated PDF. It is false by default, so a page can look correct in the browser and still have white or missing backgrounds in the PDF if the option is omitted.
Choose print or screen styles deliberately
PDF generation uses print media by default. That means @media print rules apply, and styles that only exist in screen media may not. Use page.emulateMedia({ media: 'screen' }) before page.pdf() when the intended output is the screen design. Use the default print media when the site has a purpose-built print stylesheet.
Ask the browser to preserve colors
Print rendering may adjust colors. If exact colors matter, the page’s stylesheet can request exact print color adjustment:
@media print {
html {
-webkit-print-color-adjust: exact;
}
}
For a page you control, put this rule in its stylesheet. For a third-party page, you can inject it before PDF generation:
await page.addStyleTag({
content: 'html { -webkit-print-color-adjust: exact; }',
});
await page.pdf({ path: 'page.pdf', printBackground: true });
Injecting CSS changes only the page rendered by this browser session; it does not change the website itself. A site’s own CSS or browser behavior can still affect the final appearance, so inspect representative output when color accuracy is a requirement.
3. Runnable Python example
The Python API uses snake_case option names. This script uses asynchronous Playwright and writes the generated PDF bytes to disk:
import asyncio
from pathlib import Path
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
try:
page = await browser.new_page()
await page.goto('https://example.com', wait_until='load')
# Choose this only when the desired output uses screen CSS.
await page.emulate_media(media='screen')
await page.add_style_tag(
content='html { -webkit-print-color-adjust: exact; }'
)
pdf_bytes = await page.pdf(
format='A4',
print_background=True,
)
Path('page.pdf').write_bytes(pdf_bytes)
finally:
await browser.close()
asyncio.run(main())
python -m pip install playwright
python -m playwright install chromium
python capture.py
Python’s matching options are documented in the Playwright Python Page.pdf() API. The Java API exposes corresponding setters such as setPrintBackground and setPreferCSSPageSize; see the Playwright Java Page API.
4. PDF options that affect the result
Choose a paper size and layout intentionally. These options control page geometry and the amount of content placed on each sheet; they do not replace printBackground.
| Option | Purpose and behavior |
|---|---|
format |
Paper preset, such as Letter, Legal, Tabloid, Ledger, or an ISO A-series size. It defaults to Letter and takes priority over explicit width and height. |
width, height |
Set paper dimensions explicitly. Supported units include px, in, cm, and mm; a number without a unit is treated as pixels. |
landscape |
Use landscape orientation when true; otherwise output is portrait. |
margin |
Set top, right, bottom, and left paper margins. Margins default to zero. Provide units for predictable physical measurements. |
preferCSSPageSize |
When true, CSS @page size takes priority over PDF format, width, and height. It defaults to false, which scales content to fit the chosen PDF paper size. |
pageRanges |
Select printed pages, for example '1-5, 8, 11-13'. Empty means all pages. |
scale |
Scale the rendered content. Default is 1; allowed values are 0.1 through 2. |
path |
Write the result at this path. Without it, the JavaScript API returns the PDF buffer without saving a file automatically. |
printBackground |
Include background graphics; default is false. |
Here is a print-oriented configuration using CSS page size, margins, landscape orientation, a page range, and backgrounds:
await page.emulateMedia({ media: 'print' });
await page.pdf({
path: 'report.pdf',
printBackground: true,
preferCSSPageSize: true,
landscape: true,
margin: {
top: '12mm',
right: '10mm',
bottom: '12mm',
left: '10mm',
},
pageRanges: '1-5',
scale: 1,
});
When using preferCSSPageSize: true, define the intended page size in your stylesheet, for example @page { size: A4 landscape; margin: 12mm; }. When you want Playwright’s PDF options to define the sheet size instead, leave that flag false and configure format or dimensions. Avoid setting conflicting page sizes in both places unless you have chosen which one should win.
For the complete, version-specific option list, refer to the JavaScript API reference.
5. Make capture timing and content reliable
- Navigate to the final URL. Redirects and authentication can land on a different document, so confirm the expected page before exporting.
- Wait for the content you need.
loadwaits for the load event but may not mean a client-rendered chart or delayed image is ready. Wait for a meaningful selector withawait page.locator('.report').waitFor(), or use a bounded delay when the page has a known delayed render. - Set media before printing. Emulate screen or print media before calling
pdf(), so media-dependent styles are resolved for the output. - Ensure the PDF destination is writable. Create its parent directory first if needed and use an absolute path in a service to avoid dependence on the process working directory.
- Close resources reliably. Reuse a browser across a controlled batch when appropriate, create an isolated page or context per capture, and close pages and the browser on success and failure.
For dynamic content, prefer a readiness signal tied to the actual content over waiting for every network request to stop. Analytics, streaming connections, or long polling can prevent network-idle conditions from occurring. Fonts and images may finish at different times than the initial document; if they materially affect layout, wait for the relevant assets or application state before exporting.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Backgrounds are white or missing | printBackground is omitted or false. |
Set printBackground: true (JavaScript) or print_background=True (Python). Check that the page actually uses CSS backgrounds. |
| The PDF layout differs from the browser | PDF generation applies print media and the page has print-specific styles. | Use emulateMedia({ media: 'screen' }) before pdf() for screen styles, or keep print media and adjust the site’s print CSS. |
| Colors look faded or changed | Print color adjustment can alter colors even when background graphics are included. | Add -webkit-print-color-adjust: exact to the page stylesheet or inject it for the capture, then check the resulting PDF. |
| Content is clipped or unexpectedly scaled | Paper dimensions, margins, CSS @page, and PDF settings conflict or leave too little printable area. |
Choose one source for page size; use preferCSSPageSize when CSS @page should govern. Review margins, orientation, and scale. |
| Images, charts, or fonts are absent | The page was printed before delayed rendering or asset loading completed. | Wait for the relevant locator or application-ready signal before calling pdf(). Check asset requests and authentication if content is remote. |
| Navigation times out | The site is slow, keeps connections open, or waits for a lifecycle event that is not appropriate for it. | Choose a navigation wait condition appropriate to the page, then wait for a specific content locator. Use a bounded timeout suited to the workload and log the URL and failure. |
| PDF cannot be saved | The path’s parent directory does not exist or the process lacks write access. | Create the directory and use a writable absolute path. In Python, save the returned bytes with Path(...).write_bytes(...). |
| PDF generation fails in deployment | The Playwright browser binaries are missing or the runtime cannot launch them. | Install the browser for the same Playwright version in the deployment environment and check its required system dependencies. |
7. Performance, reliability, and cost
PDF generation requires launching or reusing a browser and rendering a print layout, so capture time and memory depend on the page and runtime. Reusing a browser for a batch can avoid repeated startup work, but isolate pages or contexts and close them when finished to contain state. Bound navigation and readiness waits, limit concurrency to the capacity of the host, and avoid retry loops that repeatedly render a persistently failing page.
For repeatable output, pin the Playwright version and install its matching browser in the runtime image. Record the input URL, selected media, PDF options, and errors so a layout change can be traced. A successful PDF call does not by itself prove that every image, font, or application widget rendered as intended; validate representative pages when the output is business-critical.
Playwright is an open-source automation library; the direct operational costs of this approach are your compute, browser execution, storage, and any network traffic. This article does not assign a benchmark or per-PDF price. Large full-color backgrounds can increase PDF size and rendering work, so use only the resolution and paper size the document needs.
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. A single request returns a PNG, JPEG, WebP, or PDF; for PDF output, its API supports paper size, margins, and page ranges. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed, and response headers say what happened. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up free and capture your first 1,000 screenshots a month without a card.
9. FAQ
Does printBackground make a page use screen styles?
No. It includes background graphics in the PDF. Use emulateMedia({ media: 'screen' }) separately when you want screen media CSS.
Can I make CSS @page choose the paper size?
Yes. Set preferCSSPageSize: true and define the size in CSS. Otherwise, the configured PDF paper size takes priority and content is scaled to fit.
Can Playwright return the PDF without writing a file?
Yes. In JavaScript, call const pdf = await page.pdf({ printBackground: true }) without path; it returns a buffer. Python’s page.pdf() returns bytes.
Does the same PDF API work in every browser engine?
The cited API references describe these options, but they do not establish identical PDF output across engines or deployment environments. Check the browser engine you deploy if output consistency matters.


