Puppeteer vs Playwright for Capturing Web App Screenshots for Documentation
Both libraries capture page, full-page, and element screenshots. Compare their documented controls, then build a repeatable capture workflow with runnable examples.
Short answer: Both Puppeteer and Playwright can capture page, full-page, and element screenshots for web app documentation. Choose the library that already fits your automation stack; favor Playwright if its documented screenshot controls—masking locators, disabling animations, and applying screenshot-only styles—fit your cleanup needs, or if you need its documented Chromium, Firefox, and WebKit workflow. Puppeteer documents common output options such as clipping, transparent backgrounds, image type, and quality. The cited documentation does not establish that either is universally faster, more reliable, or easier to maintain.
This guide shows runnable Node.js examples for both libraries, the practical differences that affect documentation, ways to make captures reproducible, and common failure fixes. Official references: Puppeteer Screenshots, Puppeteer ScreenshotOptions, Playwright Screenshots, and Playwright Page API. Check the API documentation matching the versions installed in your project.
At a glance
| Need | Puppeteer | Playwright |
|---|---|---|
| Capture a page | page.screenshot() |
page.screenshot() |
| Capture the full page | fullPage: true |
fullPage: true |
| Capture one element | elementHandle.screenshot() |
locator.screenshot() |
| Documented stabilization and masking controls | Clip, transparent background, type, quality, and path options | Animation handling, locator masks, and screenshot-only styles, alongside common capture options |
| Browser engines documented in the cited sources | The cited screenshot sources do not establish an engine comparison | Page documentation names Chromium, Firefox, and WebKit |
Both meet the core capture requirement. These distinctions describe documented APIs, not a controlled comparison of output fidelity, speed, or flakiness.
Install and prepare the capture
These examples use Node.js and save image files locally. Install the library you use in your project. Puppeteer downloads a compatible browser as part of its standard installation; consult its current installation guide if your environment uses a different setup. Playwright requires installing browser binaries for the engines you intend to use.
# Choose one library
npm install puppeteer
# or
npm install playwright
# For Playwright, install browser binaries
npx playwright install
Before capturing, make the page state intentional: use seeded or stable data, fix the viewport, choose a device scale factor, and decide how to handle clocks, user-specific content, animation, and lazy-loaded media. These are general engineering practices; the library documentation does not prescribe a universal application readiness rule.
Capture with Puppeteer
Puppeteer’s screenshot guide demonstrates navigation and Page.screenshot(). This example launches a browser, sets a viewport, waits for a page-specific readiness marker, and writes a PNG. Replace the URL and selector with your application’s route and a condition that means the documentation view is ready.
// save as capture-puppeteer.mjs
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1,
});
await page.goto('http://localhost:3000/docs/example', {
waitUntil: 'networkidle2',
});
await page.waitForSelector('[data-docs-ready="true"]');
await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
await browser.close();
}
Run it with node capture-puppeteer.mjs. The networkidle2 navigation condition appears in Puppeteer’s example, but it is not a universal readiness guarantee: apps with background polling, analytics, or persistent connections may never become idle. A selector or application-owned readiness signal is often more meaningful.
Capture a Puppeteer element
const card = await page.waitForSelector('.example-card');
if (!card) throw new Error('Example card was not found');
await card.screenshot({ path: 'example-card.png' });
Puppeteer documents that an element screenshot scrolls the element into view if needed and fails if the element detaches from the DOM. Locate it after the UI is ready; if the application re-renders, locate it again before capture.
Capture with Playwright
Playwright can save a screenshot to a path or return image data. Its screenshot guide demonstrates page, full-page, and element captures.
// save as capture-playwright.mjs
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1,
});
await page.goto('http://localhost:3000/docs/example', {
waitUntil: 'domcontentloaded',
});
await page.locator('[data-docs-ready="true"]').waitFor();
await page.screenshot({ path: 'page.png', fullPage: true });
await page.locator('.example-card').screenshot({ path: 'example-card.png' });
} finally {
await browser.close();
}
Run it with node capture-playwright.mjs. The example uses Chromium. Playwright’s Page documentation also names Firefox and WebKit; install and launch the engines your project needs, then capture each with the same route state and viewport where comparisons matter.
Mask dynamic areas and stabilize screenshots
Playwright’s screenshot API documents masking locators, disabling animations, and applying a stylesheet for the screenshot. These controls can make documentation output more consistent when a region is known to vary. Use them deliberately: hiding content can also conceal a real layout or product change.
await page.screenshot({
path: 'page-stable.png',
fullPage: true,
animations: 'disabled',
mask: [page.locator('.live-avatar'), page.locator('.current-time')],
style: '.volatile-banner { visibility: hidden !important; }',
});
Check the current Page API for the exact option types supported by your installed Playwright version. Prefer masking a clearly variable value over broad selectors that could hide meaningful content.
Choose the right capture scope and output
Viewport, full page, or one element
- Viewport capture: Use the default screenshot for a screen-sized view. Fix the viewport so the same route produces comparable framing.
- Full-page capture: Set
fullPage: truein either library. Long pages can create very tall image files; split them into sections if readers or downstream tooling need manageable assets. - Element capture: Use Puppeteer’s element handle screenshot or Playwright’s locator screenshot. Ensure the target exists and has reached its final state before capturing.
- Clip capture: Puppeteer documents a clip rectangle for a defined region. Use consistent coordinates and confirm they still cover the intended content after viewport changes.
Image format, transparency, and buffers
Puppeteer’s screenshot options document output type, quality, path, clipping, and transparent backgrounds. Quality applies to JPEG and WebP style lossy output, not PNG. Playwright documents PNG, JPEG, and WebP output and can write to a path or return a buffer. Pick a format based on what consumes the file: PNG is lossless and suitable for sharp interface details; JPEG or WebP may reduce file size but can introduce compression artifacts. Validate visual acceptance for the destination rather than assuming one format is always best.
// Puppeteer: JPEG output with a quality value
await page.screenshot({ path: 'page.jpg', type: 'jpeg', quality: 85 });
// Playwright: image bytes for a later upload or comparison
const imageBytes = await page.screenshot({ type: 'png' });
For transparent output, use Puppeteer’s documented omit-background option where appropriate. Confirm the page itself does not paint an opaque background, and inspect the result against the background used by your documentation site.
Make documentation captures repeatable
- Control the data. Seed representative records and avoid live user data, changing prices, or external content when deterministic output matters.
- Fix the rendering environment. Keep browser version, viewport, device scale factor, fonts, locale, and color scheme consistent across capture runs.
- Wait for application readiness. Use a known selector, app signal, or explicit wait for required content. Do not treat a network-idle event as proof that all visual work is complete.
- Handle motion and volatile content. Disable animation, freeze time in the test environment, or mask narrowly identified changing regions. Review masks when the interface changes.
- Load below-the-fold content intentionally. Full-page capture does not guarantee every lazy image or deferred widget has loaded. Scroll through the page or trigger your app’s loading mechanism, then verify the output.
- Keep artifacts reviewable. Use stable filenames and store captures where reviewers can compare them to the expected result. Add a visual diff step only if your team has defined its acceptable differences.
These steps reduce sources of variation, but they cannot guarantee pixel-identical results across operating systems, browser versions, or font installations. The cited sources provide API controls, not a benchmark of cross-environment fidelity.
Performance, reliability, and cost
Neither the supplied documentation nor this comparison provides a controlled speed or reliability benchmark. Runtime depends on browser startup, page complexity, network behavior, image loading, and how many pages or engines you capture. Reuse a browser process for batches when your job architecture permits, while creating isolated pages or contexts for separate state. Close pages and browsers in cleanup paths, as the examples do, so failed navigation does not leave processes running.
For reliability, distinguish navigation success from screenshot success: wait for the required content, check that target elements exist, and handle timeout or detached-element errors. For CI, make the browser installation and fonts part of the environment setup, and retain failed captures or logs long enough to diagnose intermittent application state.
The libraries are software dependencies rather than per-screenshot APIs in the cited sources. Budget for the compute, browser downloads, storage, and engineering time your own capture pipeline uses; no universal cost figure follows from these documents.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Screenshot is blank or incomplete | Capture ran before the app rendered or before a required resource loaded. | Wait for an app-specific readiness selector and confirm the route has usable content before capture. |
| Navigation hangs on network idle | Background polling, analytics, or a persistent connection keeps requests active. | Use a less restrictive navigation condition, then wait for a meaningful page selector. |
| Element screenshot fails or target is missing | The selector is wrong, the UI has not rendered, or a re-render detached the element. | Wait for the locator after the final UI transition; reacquire the element after state changes. |
| Lazy images are absent below the fold | The application loads them only when they approach the viewport. | Scroll through the page to trigger loading, wait for images to finish, then capture the full page. |
| Output differs between runs | Data, time, animation, viewport, fonts, browser version, or external content changed. | Fix these inputs; use Playwright’s documented animation and masking options where suitable. |
| PNG ignores quality | Puppeteer’s quality option does not apply to PNG. | Keep PNG for lossless output, or choose JPEG/WebP and a quality value if lossy compression is acceptable. |
| Playwright cannot launch a browser in CI | The required browser binary may not be installed in the environment. | Install the browser binaries for the project’s Playwright version during environment setup. |
| Full-page output is unexpectedly huge | The page is very tall or includes large images at a high device scale. | Capture focused sections or elements, or lower the device scale factor when the documentation requirements allow. |
Which should you choose?
- Choose Puppeteer when it already fits your Node.js automation and its documented screenshot options cover the workflow: page or element capture, clipping, transparency, path, and image type or quality.
- Choose Playwright when its screenshot-specific animation, mask, or style controls fit your stabilization needs, or when your documented capture workflow needs Chromium, Firefox, or WebKit.
- Choose based on existing project setup when both meet the requirement. This research does not support a general speed, reliability, fidelity, or maintenance winner.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. One GET request returns an image or PDF; its API parameters are compatible with names used by other screenshot APIs. See the ScreenshotNeo API documentation for the available 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}`);
ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides screenshot, page information, and PDF tools for AI agents. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, with no card required.
FAQ
Can both libraries save a full page as an image?
Yes. Both document a fullPage: true screenshot option.
Can I capture just a component?
Yes. Puppeteer documents an element handle screenshot; Playwright documents a locator screenshot.
Does Playwright’s masking change the actual page?
The documented mask and screenshot-only style controls shape the capture. Review their scope so they do not hide a UI change that documentation should show.
Which library is faster?
The cited sources provide no controlled performance comparison, so choose based on your project setup and required documented controls.
