ScreenshotNeo

BlogHow-to

How to Configure Puppeteer for Browser Automation

Choose the right Puppeteer package, install its browser, set project defaults, and run reliable browser automation in Node.js.

By the ScreenshotNeo team4 October 202610 min read

Direct answer: For a new Node.js automation project, install puppeteer. It downloads a compatible Chrome for Testing browser and supplies sensible defaults. Use puppeteer-core when you manage the browser yourself or connect to a remote browser. Put persistent download and cache defaults in a Puppeteer config file; put runtime behavior such as headless mode and browser arguments in launch().

This guide follows the Puppeteer documentation surfaced for version 25.12.0. Puppeteer requirements and browser behavior can change, so check the current installation, configuration, and system requirements pages for your deployment platform before publishing or deploying version-sensitive settings.

1. Choose the package and browser

Choice Use it when What to configure
puppeteer You want a straightforward new project and a Puppeteer-managed browser. Install the package and let its install step download Chrome for Testing.
puppeteer-core Your system manages Chrome, you use a remote browser, or a hosting platform provides one. Provide executablePath for a local browser or connect using the remote-browser approach supported by your service.
System Chrome with puppeteer You need to select an installed Chrome channel. Use channel when Chrome is installed in a standard location, or set executablePath to the executable.

puppeteer downloads a recent Chrome for Testing build and, since Puppeteer v21.6.0, a separate chrome-headless-shell binary. Its browser cache defaults to $HOME/.cache/puppeteer on supported environments, a default introduced in v19.0.0. Compatibility is guaranteed with Puppeteer’s bundled browser; a different browser version may work, but is not guaranteed.

puppeteer-core does not download Chrome. Puppeteer configuration files and Puppeteer environment variables are ignored by puppeteer-core, so configure the browser explicitly in your application or browser-management layer.

2. Check Node.js and operating-system requirements

The documentation surfaced for Puppeteer 25.12.0 lists Node.js 22.12 or later. For TypeScript, it lists TypeScript 5.0.1 or later; if you type-check node_modules, target ES2022 or later. The documented Chrome for Testing platforms include Windows x64, macOS x64 and arm64, Debian/Ubuntu Linux x64 and arm64, and openSUSE/Fedora Linux x64 and arm64. Linux deployments also need the browser’s system packages.

Installation downloads are substantial. Puppeteer documentation gives approximate figures of 170 MB for macOS, 282 MB for Linux, and 280 MB for Windows. Treat these as planning estimates, not fixed package sizes. In CI or a container, account for download time, cache storage, and writable browser profile and cache directories.

3. Install Puppeteer and its browser

For a new project using npm:

mkdir puppeteer-automation
cd puppeteer-automation
npm init -y
npm install puppeteer

Equivalent package commands include:

yarn add puppeteer
pnpm add puppeteer
bun add puppeteer

If you use a managed or remote browser, install the core package instead:

npm install puppeteer-core

Package-manager policies sometimes block dependency install scripts. That can leave the Node package installed without its browser. Run Puppeteer’s browser installer explicitly after installation:

npx puppeteer browsers install

Alternatively, allow Puppeteer’s install script in your package manager’s configuration. If you change browser download settings later, run the browser installer again so the new settings take effect. See the official installation guide.

4. Run a complete browser automation script

Set the project to use ES modules by adding "type": "module" to package.json, then save this as index.js. It launches Chrome, opens a page, navigates, sets a viewport, reads the title, and closes Chrome even if an operation fails:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1080, height: 800 });
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  console.log(await page.title());
} finally {
  await browser.close();
}

Run it with node index.js. The try/finally ensures the browser process is closed when navigation or page work throws. Choose a navigation readiness condition that matches the site: waiting for every network connection to become idle can be unsuitable for pages that keep requests open.

5. Configure persistent defaults in a project file

A Puppeteer config file controls persistent defaults such as browser downloads, cache location, executable path, logging, skipped downloads, and temporary directories. Supported names include .puppeteerrc.js, .puppeteerrc.json, puppeteer.config.js, and configuration in package.json. The precise options depend on the config format and Puppeteer version; follow the current configuration reference.

For example, a JSON config can move the browser cache into a project-specific location:

{
  "cacheDirectory": ".cache/puppeteer"
}

Save that as .puppeteerrc.json. Ensure the chosen path is writable by the user running Puppeteer. Avoid putting a machine-specific executable path in a shared config unless every environment uses the same path.

Configuration and launch options have separate roles. Use config for defaults that affect installation or the project. Use launch() for runtime choices such as headless mode, arguments, profile directory, and launch timeout. Applicable environment variables override config values. Proxy variables such as HTTPS_PROXY are environment-only settings. When you change a browser download or cache setting, rerun npx puppeteer browsers install.

6. Select a browser executable when you manage Chrome

With a locally installed Chrome, use a standard channel or supply its exact executable path:

import puppeteer from 'puppeteer-core';

const browser = await puppeteer.launch({
  executablePath: '/absolute/path/to/chrome',
  headless: true,
});
try {
  const page = await browser.newPage();
  await page.goto('https://example.com');
  console.log(await page.title());
} finally {
  await browser.close();
}

Replace the sample path with the path on the target machine. With the full puppeteer package, you can also select a standard installed channel, for example channel: 'chrome', where that channel is supported and installed. Do not set both options without a specific reason. A system-managed browser may be updated independently of Puppeteer, so pin or coordinate versions when reproducibility matters.

7. Make headless and launch settings explicit

Puppeteer launches Chrome headless by default. The current documented choices are:

Setting Behavior Good fit
headless: true Modern headless Chrome. Normal automation and screenshots needing regular Chrome behavior.
headless: 'shell' The separate chrome-headless-shell binary, which does not completely match regular Chrome but is currently more performant for compatible automation. Automation that does not need the full feature set and where performance matters.
headless: false Shows a browser window. Local debugging where you need to see what the page does.

Useful launch options should solve a concrete need:

  • executablePath or channel: choose a browser you manage.
  • args: pass necessary Chrome arguments for a specific environment.
  • timeout: set how long launch may take before failing.
  • userDataDir: select a profile directory; make sure it is writable and avoid concurrent launches sharing one profile.
  • dumpio: true: forward browser process output to help diagnose launch problems.

Avoid routinely replacing Puppeteer’s default arguments. The API reference cautions that ignoreDefaultArgs should be used with care because removing defaults can break expected behavior.

8. Interact with pages using locators and built-in waits

Puppeteer recommends locators for selecting and interacting with elements. A locator click waits for the target to be in the viewport, visible, enabled, and stable across two animation frames. This avoids many timing races caused by firing an action as soon as a selector happens to exist.

const email = page.locator('input[name="email"]');
await email.fill('developer@example.com');
await page.locator('button[type="submit"]').click();
await page.locator('.success-message').wait();

Use the locator methods for common input and select fields, and visibility or function-based conditions when the page has a specific state to reach. A locator that cannot find an element or satisfy its preconditions within the timeout throws a TimeoutError. Increase a timeout only after checking the selector and expected page state.

waitForSelector() is a lower-level alternative. It returns an element handle and does not automatically retry a later action. Dispose of the handle when finished. Prefer locators for normal interactions and fixed sleeps only when the page behavior genuinely requires a known delay.

9. Make automation reliable across environments

  • Close every browser: use try/finally around page work so errors do not leave Chrome processes running.
  • Use one browser per job where practical: create pages for individual tasks, and avoid sharing a mutable page or profile between concurrent jobs.
  • Wait for page state: prefer locators and meaningful navigation conditions over arbitrary sleeps.
  • Keep paths writable: Chrome writes profile, configuration, and cache data during startup. In containers, ensure these paths belong to or are writable by the process user.
  • Match the deployment platform: install required Linux shared libraries and use a documented supported OS and architecture.
  • Preserve useful diagnostics: enable dumpio temporarily or run headful locally to understand launch and page failures.

10. Troubleshoot common setup and launch errors

Symptom Likely cause Fix
Could not find Chrome (ver. ...) The install script was blocked, or the browser was not downloaded to the configured cache. Run npx puppeteer browsers install; check the cache directory and install-script policy. Reinstall after changing download configuration.
Custom executable path fails The path is wrong, points to a wrapper rather than Chrome, or the browser version is incompatible. Verify the executable exists in the runtime environment. Prefer Puppeteer’s bundled browser when possible; compatibility with another version is not guaranteed.
Linux sandbox error The host does not provide a usable Chrome sandbox setup. Configure a usable sandbox for the host. Chrome’s sandbox protects the host from untrusted web content; --no-sandbox is strongly discouraged as a routine fix.
Container exits before Puppeteer connects Chrome cannot write profile, configuration, or cache data as the process user. Make the user-data and cache locations writable. Use a writable temporary directory such as an appropriate /tmp path where suitable.
Missing shared library or browser fails on Linux Required system packages are absent or the distribution is unsupported. Install dependencies listed for your platform in the system requirements. Chrome does not support Alpine out of the box.
Launch conflicts with enforced Chrome extension policy on Windows An enforced policy conflicts with Puppeteer’s default extension behavior. Check the documented enableExtensions workaround for this specific policy issue; do not apply it to unrelated launch failures.
Locator times out The selector is wrong, the element never reaches the expected state, or the page is slower than the timeout. Inspect the actual DOM and page state, wait for the right condition, and then adjust the timeout if the delay is expected.

For more platform-specific causes, see Puppeteer’s troubleshooting guide. Do not copy launch flags from unrelated container recipes without understanding what they change, especially flags that disable security boundaries.

11. Plan for performance, reliability, and cost

The largest setup cost for a fresh environment is often downloading and unpacking the browser. Cache the browser between CI runs when the environment permits it, and keep the cache path consistent with the config used during installation. A cache miss means another browser download; a stale or mismatched cache can cause launch failures.

Headless shell may be faster for work that does not need its missing differences, but benchmark your own workload before choosing it. Browser launch per task adds overhead; reusing a browser for multiple pages within a bounded job can reduce repeated startup work. Always close it after the job and isolate profiles when tasks need separate state.

Puppeteer itself has no per-screenshot charge. Your cost comes from compute, browser downloads, storage, and the infrastructure that runs automation. Screenshot workloads also consume CPU and memory, especially with large viewports, full-page captures, or many concurrent tabs. Set concurrency limits based on the memory and CPU available to the process, and use timeouts so a stuck page does not hold resources indefinitely.

12. Capture a page without managing a browser

If the task is to get a website screenshot rather than automate arbitrary browser behavior, ScreenshotNeo provides a screenshot API and MCP server. You can keep Puppeteer for interaction-heavy jobs and use an API call for straightforward capture jobs.

Or skip the browser setup

Use an API key from your ScreenshotNeo account. The request returns an image; save the response body as a file. See the ScreenshotNeo API documentation for the available options and formats.

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,
)
r.raise_for_status()
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 Bun.write('shot.webp', res);

ScreenshotNeo accepts cookie or consent banners like 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 and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, or another MCP client. 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 per month, with no card required.

Frequently asked questions

Does installing Puppeteer install Chrome?

The puppeteer package normally downloads a compatible Chrome for Testing browser during installation. If install scripts are blocked, run npx puppeteer browsers install. puppeteer-core does not download a browser.

Should I use puppeteer or puppeteer-core in a serverless function?

Use the package that matches how the function obtains Chrome. The full package manages its compatible browser download; use core when the runtime or a remote service manages the executable. Confirm that the runtime supports the required browser libraries, writable paths, and architecture.

Can I use Chrome installed on my computer?

Yes. Select a standard channel or pass its executable path. Puppeteer only guarantees compatibility with its bundled browser, so a system Chrome update can introduce version differences.

When should I use headless shell?

Use it only when its behavior matches your automation needs. It is a separate browser binary, does not completely match regular Chrome, and is documented as more performant for compatible automation.

Where should I put browser installation settings?

Use a project config file or the supported environment variables for persistent download and cache defaults. Use launch options for per-run choices such as visible versus headless mode and browser arguments.

References