mrkeyoor.com_
Sat 19 Sept 15:53 UTC
npmTestingupdated 19 Sept 2026

puppeteer review

Puppeteer 25.8.0 is a Node.js browser-control library for Chrome and Firefox through Chrome DevTools Protocol or WebDriver BiDi. It launches or connects to a browser, opens pages, locates elements, types and clicks, evaluates code in the page, observes network traffic, captures screenshots, and prints PDFs. The main `puppeteer` package manages a compatible Chrome download, while `puppeteer-core` leaves browser installation and executable selection to the application. This is server-side automation; our esbuild browser target failed. Version 25.8.0 adds a `followSymlinks` option, gives a recovery instruction for partially downloaded browser folders, and moves its browser manager to 3.2.1.

Verdict

Puppeteer 25.8.0 is a direct, well-documented way to drive Chrome from Node and a good building block for screenshots, PDFs, crawlers, and Chrome-focused checks. Choose a test platform for a full cross-browser suite, and treat the browser binary, sandbox, cache, and OS packages as deployment dependencies rather than npm details.

We installed it

Lab card: what happened when we installed puppeteerScreenshot of puppeteer documentation
Install✓ · 21s26 packages on disk · 30 MB
ImportESM import works · require() works · ESM package with exports map
Browsern/acould not be bundled for the browser (Node-only code, most likely)
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does puppeteer install cleanly?

Yes. In a fresh container with an empty cache, npm install puppeteer finished in 21 seconds, leaving 26 packages and 30 MB on disk. npm audit reported no known vulnerabilities.

Can puppeteer run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does puppeteer work with both ESM and CommonJS?

Yes. Both import 'puppeteer' and require('puppeteer') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does puppeteer include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

puppeteer or playwright: which should you use?

playwright: Use it for Chromium, Firefox, and WebKit testing with an integrated runner, fixtures, traces, retries, and parallel workers. Puppeteer 25.8.0 is a direct, well-documented way to drive Chrome from Node and a good building block for screenshots, PDFs, crawlers, and Chrome-focused checks.

When should you not use puppeteer?

You want a complete cross-browser test product with fixtures, assertions, retries, parallel workers, trace viewing, and reports in one package. Puppeteer is primarily the browser driver.

API stability4/5The launch, browser, page, locator, evaluation, network event, screenshot, and PDF concepts have remained recognizable across many releases. Current code can still use CommonJS or ESM loading in our Node check. The project ships majors frequently alongside browser changes, deprecates older page-level methods as protocol capabilities move, and supports two protocol paths across Chrome and Firefox. Pin both the package and deployed browser behavior, then run automation against the upgrade before merging it.
Docs5/5pptr.dev provides getting-started material, a generated class and method reference, browser management, configuration, selectors, locators, Chrome extensions, WebDriver BiDi, request interception, PDF and screenshot examples, Docker guidance, FAQ entries, and a large troubleshooting section for Linux and CI dependencies. The README now warns that package managers may block the browser download and gives the exact recovery command, which addresses a common failure before runtime.
Maintenance5/5Puppeteer 25.8.0 shipped on August 17, 2026, and its browser-management dependency received a same-day patch. GitHub reports 95,495 stars, 261 open issues and pull requests, an unarchived repository, and a push on August 24. The release adds a browser-install option and recovery guidance, while the adjacent browser package fixes Windows process behavior and partial installations. The Chrome DevTools organization maintains a fast release cadence tied to browser changes.
Ecosystem4/5npm counted 11,684,013 downloads in the latest completed week. Puppeteer works with Chrome, Firefox, DevTools Protocol, WebDriver BiDi, external test runners, browserless services, container deployments, and tools such as Chrome DevTools MCP. Bundled TypeScript declarations and working require and import paths cover common Node projects. Playwright now owns more of the all-in-one testing experience, while Puppeteer's strength remains a focused browser-control API that other tools can embed.

Use it if

  • A Node job needs Chrome-specific screenshots, PDFs, crawling, DevTools protocol access, or repeatable browser interaction.
  • The project wants Puppeteer to select and install the Chrome revision tested with the library release.
  • Browser automation is one part of a custom worker, scraper, audit tool, or test setup rather than a complete test-runner requirement.
  • Accessible-name, text, CSS, XPath, or custom selector queries need to coexist with direct page evaluation and network events.
Skip it if

Setup reality

We installed puppeteer 25.8.0 in a fresh Node 22 Bookworm container. npm succeeded in 21 seconds, left 26 packages, and used 30 MB. The package declares 6 direct dependencies, no peers, and 136 KB unpacked, with Apache-2.0 licensing and Node 22.12 or newer required. npm audit found zero known vulnerabilities. Both require() and ESM import worked, and TypeScript declarations are bundled. The esbuild browser target failed, as expected for a Node browser launcher.

The npm package and the browser are separate moving parts. puppeteer normally runs an install script to fetch compatible Chrome; several current package managers block dependency scripts unless allowed. When the executable is missing, run npx puppeteer browsers install in the deployed environment or configure the approved install script. Browser files live in Puppeteer's cache, which may not be carried from a build stage into a runtime image. A partial cache directory now produces a recovery instruction in 25.8.0.

Containers need Chrome's shared libraries, fonts, a writable profile and cache, enough shared memory, and an intentional sandbox setup. Prefer the project's supported container guidance or image. Disabling the sandbox is a security trade, not a routine fix. Run untrusted pages in isolated workers with CPU, memory, network, and time limits. puppeteer-core avoids managed downloads, but executablePath must point to a compatible browser that exists on every target host.

Navigation waits describe browser events, not application readiness. networkidle can hang on pages with analytics, streaming, or long polling, while domcontentloaded may be too early for client rendering. Wait for a specific locator or state your code owns. Locators retry actionability checks; raw element handles can go stale after rerenders. Close pages and browsers in finally, cap navigation and action timeouts, and consume request interception events exactly once or intercepted requests remain paused.

Patterns

Open a page and close the browser launch-and-navigate

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  await page.locator('h1').wait();
} finally {
  await browser.close();
}

Headless mode is the default. Wait for a page condition tied to the job instead of assuming one navigation event means all client-side work is finished.

Save a full-page image at a fixed viewport capture-screenshot

await page.setViewport({
  width: 1280,
  height: 800,
  deviceScaleFactor: 1,
});
await page.screenshot({
  path: 'page.png',
  fullPage: true,
});

Set viewport and scale explicitly so local and CI captures use the same responsive breakpoint and pixel density.

Create an A4 PDF with CSS backgrounds print-pdf

await page.goto('https://example.com/report', { waitUntil: 'networkidle2' });
await page.pdf({
  path: 'report.pdf',
  format: 'A4',
  printBackground: true,
  margin: { top: '12mm', right: '12mm', bottom: '12mm', left: '12mm' },
});

PDF uses print media rules unless you emulate another media type. Background graphics are omitted unless `printBackground` is enabled.

Use locators for a form action fill-and-submit

await page.locator('input[name=email]').fill('dev@example.com');
await page.locator('input[name=password]').fill(process.env.TEST_PASSWORD);
await Promise.all([
  page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  page.locator('button[type=submit]').click(),
]);

Start the navigation wait before the click so a fast response cannot be missed. Keep test credentials out of source and screenshots.

Locate a control through ARIA find-by-accessible-name

await page.locator('::-p-aria(Search)').fill('browser automation');
await page.locator('::-p-aria(Submit)').click();

const result = await page
  .locator('::-p-text(browser automation)')
  .waitHandle();

Puppeteer's `aria` and text selectors are useful when generated class names change. Accessible names still need to be unique enough to identify the intended control.

Pass serializable values into the page evaluate-page-code

const result = await page.evaluate((selector, taxRate) => {
  const text = document.querySelector(selector)?.textContent ?? '0';
  const subtotal = Number(text.replace(/[^0-9.]/g, ''));
  return { subtotal, total: subtotal * (1 + taxRate) };
}, '#subtotal', 0.18);

The function executes in the browser realm and cannot read surrounding Node variables. Pass inputs as arguments and return values that can cross the protocol boundary.

Map matching links into plain objects extract-list

const links = await page.$$eval('a.result', (anchors) =>
  anchors.map((anchor) => ({
    href: anchor.href,
    text: anchor.textContent?.trim() ?? '',
  })),
);

DOM nodes cannot be returned to Node as useful objects. Extract strings, numbers, arrays, and plain records while the callback is still inside the page.

Block images while continuing other requests intercept-network

await page.setRequestInterception(true);
page.on('request', (request) => {
  if (request.resourceType() === 'image') {
    request.abort();
  } else {
    request.continue();
  }
});

After interception is enabled, every request needs exactly one resolution. An exception or forgotten branch leaves the request paused and can stall navigation.

Save and restore browser cookies persist-session

import { readFile, writeFile } from 'node:fs/promises';

await writeFile('cookies.json', JSON.stringify(await browser.cookies()));

const saved = JSON.parse(await readFile('cookies.json', 'utf8'));
await browser.setCookie(...saved);

Cookie files contain session credentials. Protect them like passwords, keep them out of Git, and expect expiry or server-side revocation.

Launch a managed system Chrome use-installed-browser

import puppeteer from 'puppeteer-core';

const browser = await puppeteer.launch({
  executablePath: '/usr/bin/google-chrome',
  headless: true,
});

`puppeteer-core` does not download Chrome. Your image or host must install a compatible executable and keep its path stable across environments.

Repair a missing browser cache install-browser

npx puppeteer browsers install chrome

# Inspect available browser commands
npx puppeteer browsers --help

Run browser installation in the same user and cache context as the runtime, or configure `cacheDirectory` so build and execution agree on the location.

Bound navigation and locator waits set-timeouts

page.setDefaultNavigationTimeout(30_000);
page.setDefaultTimeout(10_000);

await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.locator('[data-ready=true]').wait();

Separate navigation and general action limits. A specific ready marker gives a clearer failure than waiting indefinitely for the network to become idle.

Alternatives

PackageRegistryPick it when
playwrightnpmUse it for Chromium, Firefox, and WebKit testing with an integrated runner, fixtures, traces, retries, and parallel workers.
webdriverionpmUse it when WebDriver, Appium, cloud device services, or a plugin-heavy test runner fit the existing test estate.
selenium-webdrivernpmUse it for standards-based control of an established Selenium Grid across several vendor browsers.

More testing guides

pytest · chai · vitest · jsdom · playwright · coverage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.