What Is Puppeteer? Definition, Architecture, Examples

Puppeteer is a Node.js library that drives Chrome and Firefox over a remote protocol. What it does, how it talks to the browser, and when to use it.

By Simon Chiu · Updated

Puppeteer is an open-source Node.js library, maintained by the Chrome team, that controls Chrome or Firefox from code through a remote-control protocol.

You write JavaScript that says "open this URL, click that button, take a screenshot", and Puppeteer turns each call into protocol messages the browser executes. It is one of the two dominant browser automation libraries for Node.js, next to Playwright. This entry is part of the screenshot glossary.

What it is in plain terms

Puppeteer is a remote control for a real browser. The browser is the same Chrome you use every day, usually started in headless mode so it has no window. Because it is a real browser, it runs JavaScript, loads web fonts, follows redirects and renders CSS exactly as a visitor would see it. That is why it became the default tool for:

  • screenshots and thumbnails of web pages;
  • HTML to PDF;
  • end-to-end tests of web apps;
  • pre-rendering JavaScript pages and scraping data that only appears after scripts run.

How Puppeteer talks to the browser

flowchart LR
  A[Your Node.js script] -->|"page.goto(), page.screenshot()"| B[Puppeteer library]
  B -->|"JSON messages over a WebSocket<br/>Chrome DevTools Protocol or WebDriver BiDi"| C[Browser process]
  C --> D[Renderer: HTML, CSS, JS]
  D -->|"PNG bytes, DOM data, events"| B

Each high-level call maps to one or more protocol commands. For Chrome the default protocol is the Chrome DevTools Protocol (CDP), the same channel DevTools uses. For Firefox, Puppeteer uses WebDriver BiDi. You can drop down to CDP when Puppeteer has no wrapper for a feature:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com');

  // Talk to Chrome directly over the DevTools Protocol, below Puppeteer's API.
  const session = await page.createCDPSession();
  const { result } = await session.send('Runtime.evaluate', { expression: 'document.title' });
  console.log('title via CDP:', result.value);

  const { data } = await session.send('Page.captureScreenshot', { format: 'png' });
  console.log('PNG bytes:', Buffer.from(data, 'base64').length);
} finally {
  await browser.close();
}

The output was title via CDP: Example Domain. page.screenshot() uses the same Page.captureScreenshot command under the hood. This shows why Puppeteer cannot produce formats Chrome does not: when we passed type: 'avif', Chrome answered Invalid image format.

The smallest useful script

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 800 });
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'example.png' });
  console.log(await browser.version()); // e.g. "HeadlessChrome/<version>"
} finally {
  await browser.close(); // always close, or Chrome processes pile up
}

Install it with npm i [email protected]. The install downloads a matching browser build into a cache folder.

The same capture in Playwright and RenderScreenshot

Playwright splits the same job across a browser, a context and a page:

import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
  await page.goto('https://example.com', { waitUntil: 'load' });
  await page.screenshot({ path: 'example-pw.png' });
  console.log(browser.version());
} finally {
  await browser.close();
}

RenderScreenshot runs the browser for you, so the capture is one HTTP request with no Chrome binary on your server:

curl -G https://api.renderscreenshot.com/v1/screenshot \
  -H "Authorization: Bearer $RS_API_KEY" \
  --data-urlencode "url=https://example.com" \
  -d width=1280 -d height=800 \
  --output example.png

Tested on 2026-10-15 with Node 22.18.0, [email protected] (Chrome 148) and [email protected] (Chromium 156). On our macOS test machine the default headless mode stalled on page.screenshot, so we ran the Puppeteer scripts with puppeteer.launch({ headless: 'shell' }). The curl request was not run for this page.

Common mistakes

  • No try/finally around the work. A thrown error skips browser.close() and leaves a Chrome process running. On a server, these add up until memory runs out. See Puppeteer timeouts and memory leaks.
  • Old tutorials. page.waitForTimeout was removed in Puppeteer v22. Wait for a real condition, or use new Promise((r) => setTimeout(r, ms)) if you must sleep.
  • Confusing the package with the browser. Upgrading Puppeteer also changes the Chrome version, which can change rendering. Pin the version you tested.
  • Missing system fonts and libraries on Linux. The browser starts, but CJK text and emoji render as boxes.
  • One browser per request. Launching Chrome is the slow part. Reuse a browser and open a new page (or context) per job.

When to use Puppeteer and when not to

Use Puppeteer when you need full control in Node.js: custom interactions, request interception, tests that drive your own app. Consider an API when the job is "URL in, image out" at volume and you would rather not run Chrome, fonts, and process cleanup yourself. The full walkthrough is in taking screenshots with Puppeteer, and the Puppeteer vs Playwright comparison helps you pick a library. To try the hosted path, create a free account.

Frequently asked questions

Is Puppeteer the same as headless Chrome?
No. Headless Chrome is the browser running without a window. Puppeteer is the Node.js library that starts that browser and sends it commands. Puppeteer can also drive a visible Chrome window.
What is the difference between puppeteer and puppeteer-core?
The puppeteer package downloads a tested browser build when you install it. puppeteer-core is the same API without the download, for when you supply your own Chrome binary or connect to a remote browser.
Can Puppeteer run in Python?
Puppeteer itself is JavaScript only. Python projects usually use Playwright for Python, which has a similar API.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.