To use Puppeteer in Node.js, install the puppeteer package, launch a browser, create a page, navigate or interact with it, and close the browser when finished. The current Puppeteer documentation snapshot lists Node.js 22.12 or newer. This guide covers installation, package selection, navigation, selectors, screenshots, PDFs, headless modes, browser connections, isolated sessions, cleanup, troubleshooting, and a no-browser API alternative.
1. Install Puppeteer and choose the right package
Create a project and initialize it as an ES-module project:
mkdir puppeteer-demo
cd puppeteer-demo
npm init -y
npm pkg set type=module
npm install puppeteer
The puppeteer package is the batteries-included option. Installation normally downloads a compatible Chrome for Testing browser, so a basic script can launch without a separate browser installation.
puppeteer-core contains the automation library but does not download Chrome. Use it when your team manages the browser binary, connects to a remote browser, or supplies a specific executable. Install it instead with:
#1 Best Overall
npm install puppeteer-core
With puppeteer-core, provide the browser arrangement explicitly, for example an executable path or a WebSocket endpoint. Do not install both packages just to make a basic script work; decide who owns the browser first.
Check Node.js and operating-system requirements
The documentation snapshot for the current release lists Node.js 22.12+. Recheck the requirement when upgrading Puppeteer because supported Node versions and browser dependencies can change. Linux systems may need additional libraries; the exact packages depend on the distribution and browser. If a launch fails on Linux, use the requirements for your release and platform rather than copying an unrelated dependency list.
When installation scripts are blocked
Some package managers or security policies block lifecycle scripts. In that case, the JavaScript package may be present while its compatible browser is missing. Follow Puppeteer’s documented browser-install command to install the browser manually, or configure the package manager to allow Puppeteer’s install script. A missing-browser error is therefore not necessarily an npm package failure.
2. Your first working Puppeteer script
Save this as index.js and run it with node index.js:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
} finally {
await browser.close();
}
The normal workflow is asynchronous: launch (or connect to) a browser, create a page, perform navigation and interaction, then clean up. The try...finally ensures the browser closes even if navigation or an assertion throws.
What each call does
puppeteer.launch()starts a browser process owned by your script.browser.newPage()creates a tab.page.goto(url)navigates that tab.page.title()reads the document title.browser.close()shuts down the launched browser and its pages.
3. Navigate, set a viewport, and save a screenshot
A page is Puppeteer’s main interaction surface. This example sets a deterministic viewport, waits for navigation to finish, and writes a PNG file:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'example.png', fullPage: true });
} finally {
await browser.close();
}
networkidle2 waits until there are no more than two active network connections. Sites with analytics, streams, or long-polling requests may never become truly idle; in those cases, wait for a specific selector or use a bounded delay that matches the page you control.
4. Interact with elements
Puppeteer supports CSS selectors and accessibility-oriented locators. Prefer a stable role, label, or test identifier over a fragile chain of classes.
Recommended Free Tools
Rank #2
Fill a form and click a result
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
await page.locator('input[name="q"]').fill('Puppeteer');
await page.locator('button[type="submit"]').click();
await page.waitForSelector('main');
console.log('Result title:', await page.title());
} finally {
await browser.close();
}
For a site whose controls expose accessible names, use an accessibility locator such as page.getByRole('button', { name: 'Search' }) or page.getByLabel('Email'). These locators express what a user sees and are generally less coupled to presentation markup.
Evaluate page-side JavaScript
const heading = await page.evaluate(() => {
return document.querySelector('h1')?.textContent?.trim() ?? null;
});
console.log(heading);
The function passed to evaluate runs in the page, not in Node.js. Pass serializable values as arguments and return serializable results; Node variables and modules are not automatically available inside the browser context.
5. Wait for the right condition
Waiting is a reliability decision. Use a condition that represents readiness rather than an arbitrary long sleep.
- Navigation: pass a suitable
waitUntilvalue topage.goto. - Element: use
page.waitForSelectoror a locator wait when a control or result must exist. - Text: wait for a locator containing the text your next action needs.
- Application state: use
page.waitForFunctionfor a narrowly scoped browser-side condition. - Known delay: use a short, explicit delay only when an animation or delayed third-party widget has no better signal.
Always set practical timeouts for your environment. A page that never resolves because of a blocked resource should fail and be diagnosed instead of holding a worker indefinitely.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
6. Headless, visible, and headless-shell modes
Puppeteer launches in headless mode by default, which is appropriate for servers and automated jobs. To watch the browser while developing, use:
const browser = await puppeteer.launch({ headless: false });
The current guide also documents headless: 'shell', which uses the separate chrome-headless-shell binary. It does not behave exactly like regular Chrome and can be useful when performance matters more than the complete feature set. Treat it as a deliberate choice, not a universal replacement for normal headless Chrome.
7. Launch a browser or connect to one
Launch when your script owns the lifecycle
const browser = await puppeteer.launch({
headless: true
});
// ...automation...
await browser.close();
This is the simplest model: each process starts its browser and closes it when the job ends.
Connect when another process owns Chrome
If a browser service or separately started Chrome exposes a WebSocket endpoint, connect rather than launching a second browser:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsRank #3
import puppeteer from 'puppeteer-core';
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');
console.log(await page.title());
} finally {
await browser.disconnect();
}
browser.disconnect() detaches your script while leaving the externally managed browser and its pages running. It is not interchangeable with browser.close(), which shuts down a browser launched or controlled by your script.
8. Isolate sessions with browser contexts
Cookies and local storage are not shared between independent BrowserContexts. Use a separate context for each account, tenant, or test case:
const context = await browser.createBrowserContext();
const page = await context.newPage();
await page.goto('https://example.com');
// ...use this isolated session...
await context.close();
Closing the context removes its pages and session state without requiring a separate browser process for every task. Do not put credentials in source code; load them from a protected secret store or environment variables.
9. Generate PDFs and control output
For a page that should be printed, call page.pdf after the content and fonts are ready:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →await page.emulateMediaType('print');
await page.pdf({
path: 'report.pdf',
format: 'A4',
printBackground: true,
landscape: false,
margin: { top: '16mm', right: '16mm', bottom: '16mm', left: '16mm' }
});
PDF pagination depends on the document’s CSS, content length, paper size, margins, and loaded assets. For repeatable output, set the viewport, wait for the content you need, and avoid capturing while fonts or images are still loading.
10. A maintainable automation pattern
For production jobs, keep browser ownership, page actions, and error reporting separate:
import puppeteer from 'puppeteer';
async function capture(url, outputPath) {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
page.setDefaultNavigationTimeout(30_000);
page.setDefaultTimeout(10_000);
await page.setViewport({ width: 1365, height: 768 });
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.screenshot({ path: outputPath, fullPage: true });
} finally {
await browser.close();
}
}
await capture('https://example.com', 'capture.png');
Reuse a browser for a batch of URLs when startup cost matters, but close each page or context after its task. For unrelated users or accounts, prefer contexts so cookies and local storage cannot leak between jobs. Record the URL, timeout, browser mode, and failure reason in your job logs.
11. Common failures and fixes
“Could not find Chrome” or an executable error
Cause: the browser download was skipped, often because an install script was blocked, or you installed puppeteer-core without supplying a browser.
Rank #4
Fix: allow the documented Puppeteer install script or run Puppeteer’s browser-install command. If using puppeteer-core, provide a valid executable arrangement or connect to a running browser.
Node.js version is rejected
Cause: the current release requires Node 22.12 or newer.
Fix: upgrade Node, or select a Puppeteer release whose documented engine requirements match your runtime. Verify the requirement again when changing versions.
Navigation times out
Cause: slow servers, a page waiting on never-ending requests, DNS or proxy problems, or a readiness condition that does not occur.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix: check the URL from the same machine, choose an appropriate waitUntil condition, wait for a concrete selector, and set a bounded timeout. Do not solve every timeout by increasing it indefinitely.
A selector is not found
Cause: the selector is wrong, the element is inside an iframe or shadow root, the page has not rendered it yet, or navigation replaced the document.
Fix: inspect the rendered page, wait for the element, use a stable role or test identifier, and handle frames explicitly when the target is not in the main document.
The screenshot is blank or incomplete
Cause: capture happened before content, images, fonts, or lazy-loaded sections were ready; the viewport also may not match the page’s responsive breakpoint.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallFix: set the viewport before navigation, wait for a meaningful selector or image state, scroll when the application lazy-loads content, and capture only after the page reaches its intended state.
The browser process remains after a crash
Cause: cleanup was skipped after an exception.
Fix: put close or disconnect logic in finally. Use close for browsers your script launched and disconnect for externally managed browsers.
12. Performance, reliability, and cost considerations
- Startup: launching a browser for every URL is simple but expensive; reuse a browser for batches while creating a fresh page or context per task.
- Isolation: contexts are lighter than separate browser processes and keep cookies and local storage separated.
- Readiness: selector- or state-based waits are usually more predictable than long fixed delays.
- Resources: full-page screenshots and PDFs consume memory for large documents. Limit concurrency and close pages promptly.
- Browser ownership:
puppeteerreduces setup work by downloading a compatible browser;puppeteer-corecan fit managed-browser infrastructure but shifts version and executable responsibility to you. - Deployment: container flags and Linux packages vary by runtime. Treat them as environment-specific, and validate the exact Puppeteer and browser versions you deploy.
Or skip the browser setup: ScreenshotNeo
If your task is simply to obtain a reliable website screenshot rather than automate a browser interaction, ScreenshotNeo provides a single HTTP request and returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
Use the API documentation at https://screenshotneo.com/docs/ for the complete option set. The service supports full-page captures with lazy images loaded, CSS-selector element shots, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and margin controls, HTML/CSS rendering, custom JavaScript and CSS, clicks before capture, selector or network-idle waits, ad/tracker/request blocking, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.
Free tools Windows power users keep installed
One-click scans. No signup required.
One request is enough:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Or from 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)
Or from 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}`);
ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to get started.
FAQ
Should I use Puppeteer or puppeteer-core?
Use puppeteer when you want installation to download a compatible browser. Use puppeteer-core when you manage the browser yourself or connect to a remote instance.
Can Puppeteer run without showing Chrome?
Yes. Headless mode is the default. Use headless: false only when you need to see the window during development or an interactive run.
What is the difference between close and disconnect?
close() shuts down the browser; disconnect() only detaches from an externally managed browser and leaves it running.
Can separate Puppeteer tasks share login cookies?
Only when they use the same browser context. Create separate BrowserContexts when each task needs isolated cookies and local storage.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

