Puppeteer Core vs. Puppeteer: Which Package Should You Use?
Puppeteer downloads a compatible browser; Puppeteer Core connects to one you manage. Compare setup, configuration, compatibility, code, and troubleshooting.
Short answer: install puppeteer when you want Puppeteer to download and manage a compatible browser with sensible defaults. Install puppeteer-core when your application already manages Chrome or Firefox, connects to a remote browser, or needs explicit control over the executable and launch process. Core does not download Chrome, and Puppeteer configuration files and configuration environment variables do not configure it.
Both packages expose the Puppeteer automation API. The practical difference is who owns the browser binary and how configuration reaches it. This guide compares those choices, shows complete JavaScript examples, explains browser compatibility, and covers the failure modes that appear in production.
What is the difference?
| Concern | puppeteer |
puppeteer-core |
|---|---|---|
| Browser download | Downloads a compatible browser during installation by default. | Does not download Chrome. |
| Browser ownership | Puppeteer manages the browser it downloaded. | You install, select, or connect to the browser. |
| Typical use | Local scripts, CI jobs, and applications wanting a conventional setup. | Remote browsers, custom images, system browsers, and managed infrastructure. |
| Configuration | Supports Puppeteer configuration files and configuration environment variables. | Those configuration files and environment variables are ignored; use the programmatic API. |
| Compatibility | The package release is paired with a browser release. | You must ensure the browser you provide is compatible with the package version. |
These are separately published packages, not two installation modes for one package. See the official installation guide and configuration guide.
Choose puppeteer when Puppeteer should own the browser
- You want the quickest local setup.
- Your build can download a browser binary during installation.
- You do not need to share a remote browser pool.
- You prefer Puppeteer’s defaults and configuration files.
Install it with:
npm install puppeteer
Complete screenshot example:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'example.png', fullPage: true });
} finally {
await browser.close();
}
})();
The downloaded browser is selected to work with the Puppeteer API. If your package manager blocks install scripts, use Puppeteer’s browser-install command described in the installation documentation.
Choose puppeteer-core when you own the browser
Core is the better fit when a platform team supplies a browser, when Chrome runs in a separate service, or when you need to pin an operating-system browser yourself.
npm install puppeteer-core
Launch an installed browser with executablePath
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.launch({
headless: true,
executablePath: '/usr/bin/google-chrome',
args: ['--no-sandbox']
});
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'example.png' });
} finally {
await browser.close();
}
})();
Use the actual path supplied by your operating system or container image. Core does not infer a downloaded browser for you.
Select a browser channel when supported
const puppeteer = require('puppeteer-core');
const browser = await puppeteer.launch({ channel: 'chrome', headless: true });
await browser.close();
Use a channel only when that browser installation exists on the machine. Otherwise provide executablePath or connect to a remote endpoint.
Connect to a remote browser
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.connect({
browserWSEndpoint: process.env.BROWSER_WS_ENDPOINT
});
try {
const pages = await browser.pages();
const page = pages[0] || await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.screenshot({ path: 'remote.png' });
} finally {
await browser.close();
}
})();
When you connect to a shared service, follow that service’s lifecycle rules. In some environments you disconnect rather than terminate the shared browser.
Configuration differences that cause surprises
Puppeteer reads its supported configuration files and environment variables. puppeteer-core ignores them, so settings such as a browser download location or executable selection must be passed in code.
| Need | Recommended approach with Puppeteer | Required approach with Core |
|---|---|---|
| Choose a browser binary | Use documented configuration or launch options. | Pass executablePath, channel, or a remote endpoint programmatically. |
| Control downloads | Use Puppeteer’s configuration and browser-install tooling. | Install and cache the browser outside Core. |
| Run in CI | Cache the downloaded browser and permit install scripts. | Build the browser into the image and pin its path. |
Read the configuration documentation before copying a puppeteer setup into a Core project.
Browser compatibility is versioned
Puppeteer releases are tightly bundled with browser releases to protect compatibility with the Chrome DevTools Protocol and WebDriver BiDi. A Core project therefore has an extra responsibility: the browser you provide must match the package’s supported range.
- Record the Puppeteer or Core version in your lockfile.
- Check the project’s supported-browser mapping for that release.
- Pin the browser image, channel, or executable used in production.
- Upgrade the package and browser as a tested pair.
The exact supported versions change with releases, so do not rely on an old mapping. The official FAQ explains why a particular Puppeteer version may not work with a particular Chrome or Firefox version.
Migration checklist
From puppeteer to puppeteer-core
- Replace the dependency and imports.
- Provision a compatible browser in your image or browser service.
- Add
executablePath,channel, orbrowserWSEndpoint. - Move browser-selection settings from configuration files into launch or connect code.
- Verify sandbox, fonts, certificates, proxy, and viewport behavior in the target environment.
- Pin and document the package/browser pair.
From Core to puppeteer
- Install
puppeteerand allow its install step to fetch the browser. - Remove assumptions about a system executable path.
- Retain explicit launch options that your workload still needs.
- Cache the downloaded browser in CI to avoid repeated downloads.
Performance, reliability, and cost
- Installation time:
puppeteermay spend time downloading a browser; caching the browser directory makes repeat builds faster. Core shifts that work to image creation or browser infrastructure. - Cold starts: launching a local browser costs startup time in either package. A managed remote browser can amortize installation, but adds connection and network latency.
- Reliability: Puppeteer-managed binaries reduce accidental browser drift. Core gives you control, but an operating-system update can silently change the browser unless you pin it.
- Resource use: reuse a browser process carefully and create isolated pages or contexts per job. Close pages and browsers in
finallyblocks. - Cost: neither npm package has a usage fee. Your costs come from CI minutes, storage, browser hosts, and any remote-browser service.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Could not find Chrome or a missing executable error |
Core was installed without a browser, or the path is wrong. | Install a compatible browser and pass its absolute executablePath, use a valid channel, or connect remotely. |
| Configuration changes have no effect | The project uses puppeteer-core. |
Move the setting into programmatic launch/connect options. |
| Browser revision or protocol errors | Package and browser versions are incompatible. | Check the supported-browser mapping and upgrade or pin them as a pair. |
| Install completes but no browser is present | Your package manager blocked installation scripts. | Permit the install step or run Puppeteer’s documented browser-install command. |
| Works locally, fails in a container | Missing executable, shared libraries, fonts, permissions, or sandbox support. | Use a container image with the required browser dependencies, verify the path, and configure sandboxing according to your runtime. |
| Remote connection closes unexpectedly | Expired endpoint, network policy, or a remote service terminating idle sessions. | Check endpoint lifetime and connectivity, add bounded retries, and create a fresh connection per failed session. |
| Pages hang indefinitely | Waiting for a network-idle condition on a page with long-lived requests. | Choose a less strict waitUntil, add an explicit timeout, and wait for the selector that represents readiness. |
Or skip the browser setup
If your goal is a clean website screenshot rather than browser infrastructure, ScreenshotNeo provides a single HTTP request for PNG, JPEG, WebP, or PDF output. The API accepts the URL and capture options, while its browser service handles setup.
See the ScreenshotNeo API documentation. cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. An MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Can I install both packages?
You can, but most applications should depend on one deliberately. Mixing them can make browser ownership and version selection unclear.
Does Core support the same page APIs?
Core is the programmatic Puppeteer library for driving a browser, so the automation API is comparable. The difference is browser provisioning and configuration behavior.
Which package is better for a Docker image?
Use Puppeteer when the image should fetch and manage its compatible browser during installation. Use Core when your image explicitly contains the browser and its dependencies.
Should I use Firefox?
Puppeteer documents Chrome and Firefox support, but exact supported versions are release-sensitive. Check the supported-browser page for the package version you deploy.
Does Core make screenshots faster?
Not inherently. Core can reduce install work in a prebuilt image, while capture speed depends on browser startup, page behavior, and your infrastructure.
