ScreenshotNeo

BlogGuides

Puppeteer: A Guide to Browser Automation and Screenshots

Learn how to install Puppeteer, automate Chrome and Firefox, capture pages and elements, and troubleshoot screenshot workflows.

By the ScreenshotNeo team4 October 20269 min read

Puppeteer is a JavaScript library for automating browsers. To capture a screenshot locally, install puppeteer, launch its compatible browser, navigate to a URL, and call page.screenshot(). Use puppeteer-core when you manage the browser installation yourself or connect to a remote browser. This guide covers setup, page and element captures, options, readiness, reliability, troubleshooting, and when a hosted screenshot API can simplify the workflow.

1. Choose the right Puppeteer package

Package Use it when Browser setup
puppeteer You want Puppeteer’s standard local setup. Downloads a compatible Chrome for Testing and chrome-headless-shell by default.
puppeteer-core You connect to a remote browser or install and manage the browser separately. Does not download Chrome. Configure the executable path, channel, or remote connection yourself.

The full package is the simplest starting point for local automation. The browser download is substantial: the Puppeteer 25.12.0 installation documentation estimates about 170 MB on macOS, 282 MB on Linux, and 280 MB on Windows. Treat these as documentation estimates, not fixed download sizes. The default browser cache is ~/.cache/puppeteer. Puppeteer installation guide.

2. Install Puppeteer and check requirements

The commands below install the full package. Choose the package manager already used by your project:

npm install puppeteer
# or
yarn add puppeteer
# or
pnpm add puppeteer
# or
bun add puppeteer

As checked on October 3, 2026, Puppeteer’s requirements page specifies Node.js 22.12 or later and TypeScript 5.0.1 or later for TypeScript projects, in addition to platform-specific browser requirements. These requirements can change; check the current system requirements before upgrading or setting up a new CI image.

If a package manager blocks install scripts, Puppeteer’s browser download may not run. Allow the install script or use Puppeteer’s browser installation command after adding the package. See the installation guide for the current command and package-manager instructions.

3. Capture a page with JavaScript

This CommonJS script opens a page, waits for navigation to reach networkidle2, saves a full-page PNG, and closes the browser even if navigation or capture fails. Save it as screenshot.cjs and run node screenshot.cjs https://example.com.

const puppeteer = require('puppeteer');

async function main() {
  const url = process.argv[2] || 'https://example.com';
  const browser = await puppeteer.launch({ headless: true });

  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 1440, height: 1000, deviceScaleFactor: 1 });
    await page.goto(url, { waitUntil: 'networkidle2', timeout: 60_000 });
    await page.screenshot({ path: 'page.png', fullPage: true });
    console.log('Saved page.png');
  } finally {
    await browser.close();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

For an ES module, use import puppeteer from 'puppeteer'; and put the script in a project configured for ES modules, or use an .mjs file. The screenshot guide’s basic workflow is launch, create a page, navigate, capture, and close. Official screenshot guide.

Choose a navigation wait state deliberately

  • domcontentloaded waits for the initial document to be parsed. It is often faster, but client-rendered content may not be ready.
  • load waits for the page load event and its dependent resources. Some sites continue loading afterward.
  • networkidle0 and networkidle2 wait for network activity to quiet to different thresholds. Analytics, polling, and long-lived requests can make network-idle waits slow or unsuitable.

These are navigation milestones, not universal guarantees that the exact content you need has rendered. For an application, wait for a selector that marks the desired content as ready; for a known animation or delayed widget, wait for an appropriate duration. The right condition depends on the page.

4. Capture an element instead of the whole page

Use an element handle’s screenshot method when you need a chart, card, or other selected region. The method attempts to scroll a hidden element into view before capture.

const puppeteer = require('puppeteer');

async function main() {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
    await page.waitForSelector('main article', { visible: true, timeout: 15_000 });
    const article = await page.$('main article');
    if (!article) throw new Error('Could not find main article');
    await article.screenshot({ path: 'article.png' });
  } finally {
    await browser.close();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Choose a selector stable enough for automation. Prefer semantic structure or a deliberate test attribute over generated class names that may change between builds. Element screenshot guidance.

5. Select screenshot scope and output options

Option What it controls Notes
path Writes the image to a file. When supplied, the extension can determine the image type.
type Image format such as PNG, JPEG, or WebP where supported by the installed Puppeteer/browser version. Set it explicitly when you do not want the path extension to decide.
fullPage Captures the full page rather than only the viewport. Very long pages can create large images and use more memory.
clip Captures a specified rectangular region. Useful for a fixed region; ensure the coordinates and dimensions match the page layout.
quality Controls lossy image quality for formats that support it. Does not apply to PNG.
omitBackground Omits the default white background to allow transparency. Useful for transparent page or element captures when page styling permits it.
// Viewport screenshot, JPEG output
await page.screenshot({ path: 'viewport.jpg', type: 'jpeg', quality: 85 });

// Full-page screenshot
await page.screenshot({ path: 'full.png', fullPage: true });

// Clip a rectangle in page coordinates
await page.screenshot({ path: 'region.png', clip: { x: 40, y: 80, width: 640, height: 360 } });

// Preserve transparency where possible
await page.screenshot({ path: 'transparent.png', omitBackground: true });

Consult the ScreenshotOptions API reference for the exact options supported by your installed version. Format and option support can depend on the browser and Puppeteer version.

6. Make the capture match the intended page state

A screenshot records a browser’s current rendered state. Control the inputs that affect that state so repeated captures are useful:

  1. Set the viewport before navigation. Layout and responsive breakpoints depend on viewport dimensions. Set a device scale factor when output pixel density matters.
  2. Wait for the content you need. Use waitForSelector for a specific element, rather than assuming the navigation event means a client-rendered application is finished.
  3. Control dynamic content. If a page has animations, rotating banners, personalized data, or delayed image loading, decide whether to wait, disable animations through page styling, or capture after a known state.
  4. Use full-page capture when the deliverable needs below-the-fold content. A normal page screenshot covers the viewport; fullPage: true expands the capture to the page.
  5. Close resources. Close pages or the browser in a finally block so failed navigation does not leave browser processes running.

For long pages with lazy-loaded images, a full-page screenshot may not show assets that only load as the page is scrolled. If those assets matter, scroll through the page and wait for them to load before capturing, or use an appropriate page-specific readiness condition. Avoid assuming one delay or network-idle setting will work for every site.

7. Browser compatibility and protocol notes

Puppeteer’s supported-browser table is versioned. The documentation checked October 3, 2026 lists Puppeteer 25.12.0 with Chrome for Testing 154.0.8037.57 and Firefox 156.0.1. This is a release mapping, not a promise that any arbitrary browser build will behave identically. If a release is not listed, Puppeteer’s table says to use the browser listed for the immediately prior Puppeteer version. Check the supported browsers table for the version you install.

Puppeteer uses Chrome DevTools Protocol (CDP) by default for Chrome and WebDriver BiDi by default for Firefox; it also supports BiDi for Chrome. API support can vary by protocol, so confirm support for the browser and protocol you plan to use. Since Puppeteer v20, its Chrome download is Chrome for Testing, which supports headless and headful operation; the older headless implementation is available separately as chrome-headless-shell. Since v23, Puppeteer downloads and works with stable Firefox. See the official FAQ and browser table.

8. Run Puppeteer in CI or at higher volume

Local execution is a direct fit for learning the API and controlled workloads. When running in CI or capturing many pages, plan for the browser download, compatible runtime and libraries, process cleanup, parallelism, and output storage.

  • Keep browser and Puppeteer versions aligned. Upgrade together and use the documented browser mapping when diagnosing differences.
  • Limit concurrency to available resources. Each browser session consumes memory and CPU. Increase parallel work gradually and monitor resource limits instead of launching an unbounded number of pages.
  • Use timeouts and cleanup. Bound navigation and selector waits, close browser instances in cleanup paths, and capture enough error context to identify the failed URL or step.
  • Make outputs reproducible. Set viewport and relevant state, keep output names unique for parallel jobs, and avoid depending on ephemeral local files in later workflow stages.
  • Use remote browser infrastructure only when its benefits fit. Compare browser/OS coverage, concurrent sessions, CI integration, debugging artifacts, network access to staging sites, infrastructure work, and current pricing.

BrowserStack describes cloud browser testing for Puppeteer with parallel execution, CI integrations, and debugging features. Its product page has claimed tests can run 30x faster through parallel testing; that is a vendor claim, not an independent benchmark. Browserless offers managed browsers reachable through Puppeteer or Playwright over WebSocket, along with REST and GraphQL routes for tasks including screenshots and PDFs. Its testing page has advertised a $25/month starting price; check current plan terms before relying on that figure. These are optional vendor examples, not requirements for using Puppeteer. BrowserStack Automate, BrowserStack Puppeteer integration, Browserless, and Browserless pricing.

9. Troubleshooting common Puppeteer screenshot problems

Symptom Likely cause Fix
Chrome executable not found after install Package install scripts were blocked or the browser cache is missing. Allow the Puppeteer install script or run its documented browser-install command. Confirm the cache location and installed Puppeteer version.
Launch fails in a container or CI runner Missing system libraries, incompatible platform requirements, or an unsuitable browser executable. Check the current system requirements for the OS and install required dependencies. Confirm the browser build matches Puppeteer.
Navigation times out The site is slow, a request never settles, or the chosen wait condition is too strict for a page with ongoing traffic. Use a reasonable explicit timeout, select a more appropriate navigation milestone, then wait for the specific content needed. Diagnose network access and the target site’s response separately.
Screenshot is blank or missing expected content Capture happened before client rendering, content is below the fold and lazy-loaded, or a selector did not match the intended element. Wait for a visible content selector, inspect the matched element, and scroll through lazy-loaded regions when needed.
Element capture fails or captures the wrong area The selector is absent, hidden, unstable, or points to a different element than expected. Wait for the selector, verify it is visible, use a stable selector, and check the element’s bounding box and page state.
Output format or quality is unexpected The path extension, explicit type, and quality settings do not agree, or the format does not support quality control. Set the desired type explicitly and use quality only for a lossy format that supports it; quality does not apply to PNG.
Browser processes accumulate An error path skipped browser closure. Put browser.close() in a finally block and avoid abandoning launched browser instances after a timeout.

10. Or skip the browser setup

If you need an image or PDF from a URL without installing and maintaining a browser, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request can return a PNG, JPEG, WebP, or PDF. 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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

Cookie banners are accepted like a visitor and removed before the shot, along with more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up free and get 1,000 screenshots a month with no card.

11. Frequently asked questions

Can Puppeteer take a screenshot without opening a visible browser window?

Yes. Puppeteer supports headless browser operation. Set headless: true when launching, as in the examples.

Should I use Puppeteer or Puppeteer Core?

Use puppeteer for its managed local browser download. Use puppeteer-core when your application provides the browser or connects to a remote browser.

Does Puppeteer support Firefox?

Yes. Puppeteer’s supported-browser documentation covers Chrome and Firefox. Check its release table and protocol support for your installed version.

Can a screenshot include transparent pixels?

Use omitBackground: true to omit the default white background. The page’s own styles and the chosen capture region still affect the result.

Is a hosted browser required to capture screenshots?

No. Puppeteer can run locally with its compatible browser. A hosted browser is an optional choice when its browser coverage, parallel capacity, or managed infrastructure suits your workflow.