How to take a Playwright screenshot of a PDF viewer page
Capture a rendered PDF viewer with Playwright, understand the headless navigation limitation, and choose a reliable path for a viewport image or full document.
To capture the currently visible part of a PDF viewer, take a screenshot after the document has rendered:
await page.screenshot({ path: 'pdf-viewer.png' });
This captures the browser page viewport. It does not guarantee that every page in the PDF will be captured. Playwright’s Page API documentation states: “Headless mode doesn’t support navigation to a PDF document.” If you need a particular PDF page or the entire document, use a workflow that renders PDF pages explicitly instead of relying on fullPage: true.
For ordinary HTML pages, Playwright can capture the viewport, the full scrollable page, or a selected element. Those APIs do not promise access to every page or internal control of the browser’s built-in PDF viewer. Verify the output in the browser mode and configuration you use. See the Playwright Page API and Screenshots guide.
1. Choose the capture workflow
| What you need | Approach | What to expect |
|---|---|---|
| The visible viewer viewport | page.screenshot() after rendering |
Captures the current browser page image. Check that the PDF page has appeared. |
| A specific HTML viewer element | locator.screenshot() |
Captures the selected DOM element if the viewer exposes a suitable element. |
| A long, scrollable HTML page containing a viewer | page.screenshot({ fullPage: true }) |
Captures the full scrollable web page. It is not a documented way to enumerate every PDF sheet. |
| A particular PDF page or every page | Render PDF pages explicitly, or use an HTML PDF viewer with controls you can automate | Gives explicit control over which page is rendered. The built-in viewer’s internals are not guaranteed by the generic screenshot API. |
Direct navigation to a PDF deserves special care: the Playwright Page API documents that headless mode does not support navigation to a PDF document. This caveat is about direct PDF navigation in headless mode; it does not establish that every screenshot operation or every browser mode fails for every viewer.
2. Set up a runnable Playwright script
Install Playwright and its Chromium browser:
npm install playwright
npx playwright install chromium
Save this as capture-viewer.js. Set PDF_VIEWER_URL to the URL of the HTML page that hosts your PDF viewer. The script waits for the page to load, allows time for the viewer to render, then captures the viewport.
const { chromium } = require('playwright');
(async () => {
const url = process.env.PDF_VIEWER_URL;
if (!url) throw new Error('Set PDF_VIEWER_URL to the HTML PDF viewer page URL');
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
await page.goto(url, { waitUntil: 'load', timeout: 60_000 });
// Replace this delay with a viewer-specific readiness check when available.
await page.waitForTimeout(1500);
await page.screenshot({ path: 'pdf-viewer.png' });
} finally {
await browser.close();
}
})();
Run it with:
PDF_VIEWER_URL='https://your-site.example/document-viewer' node capture-viewer.js
The example deliberately targets an HTML viewer page rather than claiming that direct headless navigation to a PDF URL is supported. If your viewer exposes a stable element containing the rendered page, replace the final capture with a locator screenshot:
await page.locator('.pdf-page').screenshot({ path: 'pdf-page.png' });
.pdf-page is only an example selector. Inspect your viewer’s DOM and use a selector that it actually exposes. Native viewer surfaces may not be ordinary DOM elements, so check the resulting image rather than assuming a locator can reach them.
3. Screenshot API options for viewer pages
These options apply to the documented generic page and element screenshot APIs. Their presence does not imply special support for built-in PDF viewer internals.
- Viewport:
page.screenshot({ path: 'shot.png' })captures the current viewport. - Full scrollable page:
page.screenshot({ path: 'shot.png', fullPage: true })captures the full scrollable web page. Do not interpret this as a guarantee that all PDF sheets will be included. - Element:
page.locator('selector').screenshot({ path: 'element.png' })captures a selected element when the viewer exposes it in the DOM. - Format: the screenshot API supports PNG, JPEG, and WebP output. Use a matching file extension and select the format appropriate to your downstream workflow.
- Scale: screenshot scale options control output scaling. Choose the API-supported scale that matches whether you need CSS-pixel output or a higher-density image; check the Page API reference for the current option names and behavior.
For deterministic captures, set the viewport explicitly, wait for a viewer-specific signal when possible, and avoid treating a fixed delay as proof that rendering has finished. Browser updates and viewer implementations can change how a PDF surface is exposed.
4. If you need the whole PDF or a chosen page
Decide whether the deliverable is a screenshot of a viewer or an image of PDF content. For an image of a particular page or every page, use a rendering workflow that gives explicit page selection and rendering control, such as an HTML PDF viewer you can automate or a PDF-to-page-image pipeline. Then capture the rendered page or image. This avoids assuming that a browser’s full-page screenshot will traverse the document’s sheets.
Playwright’s page.pdf() is a separate operation: it generates a PDF from a webpage, using print CSS by default. To generate that webpage PDF with screen media, call page.emulateMedia({ media: 'screen' }) before page.pdf(). It is not the method for making a PNG screenshot of a PDF viewer.
If you are doing visual regression testing, Playwright Test’s expect(page).toHaveScreenshot() is an assertion that waits for two consecutive screenshots to match before comparing against the expected snapshot. Use it for a stable page or viewer state, and ensure the viewer has rendered the intended content before relying on the comparison.
5. Reliability, speed, and output checks
- Wait for actual readiness: prefer a viewer-specific event or visible page element over a guessed delay. A PDF may still be rendering after the host page’s load event.
- Check the artifact: open the saved image and confirm it contains the expected page, scale, and viewer state.
- Keep capture dimensions controlled: use a fixed viewport for repeatable results. A full-page capture can be much larger than a viewport image on long HTML pages.
- Separate document rendering from screenshotting: if you need several pages, render or navigate to each page deliberately, then capture each one. Do not assume one screenshot call will return a multipage document.
- Keep browser lifecycle cleanup in place: close the browser in a
finallyblock so failures do not leave browser processes running.
The research sources do not provide a universal timing benchmark for PDF viewer rendering. The right wait condition depends on the viewer and document; measure it in your own environment and use a readiness signal where possible.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Navigation to a direct PDF URL fails in headless mode | Playwright documents that headless mode does not support navigation to a PDF document. | Automate an HTML viewer page or use a workflow that renders PDF pages explicitly. |
| Screenshot is blank or shows a loading state | The viewer had not rendered the page when the screenshot was taken. | Wait for a viewer-specific readiness signal or a visible rendered page; inspect the saved image. |
fullPage: true captures only the viewer page or visible content |
fullPage concerns the full scrollable web page; it is not a documented all-PDF-pages option. |
Render or navigate to PDF pages explicitly and capture them individually. |
| Locator screenshot cannot find the page surface | The viewer may render its surface outside the ordinary DOM or use a different selector. | Inspect the page DOM and viewer implementation; use a capture workflow that exposes the rendered page. |
page.pdf() produces a PDF instead of an image |
page.pdf() prints a webpage to PDF; it is not a screenshot API. |
Use page.screenshot() for an image of the current page, or render PDF pages to images for document-page output. |
| Visual snapshot changes between runs | The viewer may still be rendering or the browser viewport and state may differ. | Fix the viewport, wait for a stable viewer state, and use Playwright Test’s screenshot assertion for visual regression workflows. |
7. Or skip the browser setup
If you need a screenshot of a web page, ScreenshotNeo provides a one-request screenshot API. It captures a URL as PNG, JPEG, WebP, or PDF. For a page that hosts a PDF viewer, use its viewer-page URL; this does not promise to capture every sheet of a PDF opened directly in the built-in browser viewer. See the ScreenshotNeo API documentation.
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}`);
Replace the example target URL with your viewer page. ScreenshotNeo accepts cookie or consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.
8. FAQ
Can Playwright take a screenshot of the PDF page currently visible in a viewer?
It can screenshot the current browser page. Try it after the viewer renders, then inspect the image in the browser configuration you use.
Does fullPage: true capture every page in a PDF?
There is no such guarantee in the cited Playwright screenshot documentation. It means the full scrollable web page.
Does Playwright’s PDF output option make an image of a PDF viewer?
No. page.pdf() creates a PDF from a webpage. Use the screenshot API for a page image.
Can I capture a PDF opened directly in Chromium headless?
The Page API says headless mode does not support navigation to a PDF document. Use an HTML viewer or explicit PDF page rendering when you need predictable page access.


