How to Run Puppeteer in a Browser
Run Puppeteer from browser-side JavaScript by bundling its browser entry point and connecting to an already-running browser over WebSocket.
Short answer: You can run Puppeteer API calls from browser-side JavaScript, but the browser page does not launch Chromium itself. Bundle the browser-specific entry point from puppeteer-core, then connect it to a separate, already-running browser through a valid WebSocket endpoint. The remote browser performs the automation; the page hosts the client code.
This setup is useful when a web application needs to control a browser session through a browser-accessible endpoint. If your application needs to launch or install the browser itself, use Puppeteer from Node.js instead.
1. Understand the two browser arrangements
“Run Puppeteer in a browser” means the Puppeteer client API runs in a web page and sends commands to another browser. It does not mean embedding a browser process in the page. The remote browser must already be running, expose an open debugging connection, and be reachable at a valid WebSocket endpoint.
| Question | Browser-side client | Node.js client |
|---|---|---|
| Where does Puppeteer code run? | In a web page, as a bundled browser script | In a Node.js process |
| Who starts the browser? | A separate service or process must start it | Your Node.js application can launch it |
| How does it connect? | WebSocket endpoint for the running browser | Usually launches or connects from the Node process |
| Can it download a browser? | No; that depends on Node.js APIs | Yes, depending on package and installation setup |
The [official browser guide](https://pptr.dev/guides/running-puppeteer-in-the-browser) describes the browser-page arrangement and its separate browser requirement.
2. Install and bundle the browser entry point
Use puppeteer-core for the browser-side client. The documented browser entry point is puppeteer-core/lib/puppeteer/puppeteer-core-browser.js. A bundler such as Rollup or Webpack is needed to produce a browser-ready file.
npm install puppeteer-core rollup
Create src/client.js:
import puppeteer from 'puppeteer-core/lib/puppeteer/puppeteer-core-browser.js';
const wsUrl = window.PUPPETEER_WS_ENDPOINT;
if (!wsUrl) {
throw new Error('Set window.PUPPETEER_WS_ENDPOINT to a valid browser WebSocket endpoint.');
}
async function inspectRemoteBrowser() {
const browser = await puppeteer.connect({ browserWSEndpoint: wsUrl });
try {
const pages = await browser.pages();
console.log(`Connected; open pages: ${pages.length}`);
return pages.length;
} finally {
// Disconnect this client without closing the remote browser process.
browser.disconnect();
}
}
inspectRemoteBrowser().catch(error => {
console.error('Could not connect to the remote browser:', error);
});
Configure the endpoint in the page through your application’s own configuration mechanism; do not put a real, unprotected debugging endpoint into public source code. The documentation requires a valid endpoint but does not prescribe a particular hosting or authentication setup.
Create rollup.config.js:
import { nodeResolve } from '@rollup/plugin-node-resolve';
export default {
input: 'src/client.js',
output: {
file: 'public/puppeteer-client.js',
format: 'iife',
name: 'PuppeteerClient'
},
plugins: [nodeResolve({ browser: true })]
};
Install the resolver and build:
npm install --save-dev @rollup/plugin-node-resolve
npx rollup -c
Include the resulting file in your page after defining the endpoint:
<script>
window.PUPPETEER_WS_ENDPOINT = window.APP_CONFIG.browserWebSocketEndpoint;
</script>
<script src="/puppeteer-client.js"></script>
Adapt the configuration to your project’s bundler and module setup. Puppeteer’s guide also notes that the WebDriver BiDi mapper can be excluded to reduce bundle size if you do not need WebDriver BiDi.
3. Use the connected browser
Once connected, browser-side Puppeteer can perform supported operations against the remote browser. The guide lists page creation and closing, navigation, JavaScript evaluation, screenshots and PDFs, cookie inspection and modification, and network monitoring or interception.
import puppeteer from 'puppeteer-core/lib/puppeteer/puppeteer-core-browser.js';
async function captureCurrentPage(wsUrl) {
const browser = await puppeteer.connect({ browserWSEndpoint: wsUrl });
try {
const pages = await browser.pages();
const page = pages[0] ?? await browser.newPage();
if (!page.url() || page.url() === 'about:blank') {
await page.goto('https://example.com');
}
const title = await page.title();
const screenshot = await page.screenshot({ type: 'png' });
return { title, screenshot };
} finally {
browser.disconnect();
}
}
This example returns screenshot bytes to its caller; how you display or upload those bytes depends on your application. Use browser.disconnect() when the client should end its connection while leaving the remotely managed browser available. The example does not close the shared browser process.
4. Choose the right package and browser version
| Choice | Use it when | Browser responsibility |
|---|---|---|
puppeteer |
You want the standard Node.js workflow and Puppeteer-managed compatible Chrome | Installation normally downloads a compatible Chrome |
puppeteer-core |
You manage the browser yourself or connect to a remote browser | You supply/manage the browser; connection setups need the endpoint |
The [installation guide](https://pptr.dev/guides/installation) explains package installation and browser downloads; the [project overview](https://pptr.dev/) shows the Node.js launch workflow. If an install script is blocked, the installation guide documents manually installing the browser with npx puppeteer browsers install or allowing Puppeteer’s install script. That local browser-install issue is separate from the browser-side requirement for a reachable WebSocket endpoint.
Browser protocol compatibility is version-sensitive. Puppeteer releases are associated with browser versions; consult the current [supported browser versions](https://pptr.dev/supported-browsers) instead of assuming any installed browser will work. The current FAQ says Puppeteer v23.0.0 onward supports Chrome and Firefox, with CDP as Chrome’s default and WebDriver BiDi as Firefox’s default. Check the live [FAQ](https://pptr.dev/faq) for current protocol and support details before choosing versions.
5. Troubleshoot common failures
| Symptom | Likely cause | What to check |
|---|---|---|
| “Cannot find module” for the browser entry point | The package is missing, the import path differs in the installed version, or the bundler did not resolve package files | Install puppeteer-core, confirm the documented browser entry point exists for that release, and check bundler resolution. |
Node built-in module or process errors in the bundle |
A Node-targeted entry point or configuration was bundled for the page | Import the browser-specific entry point and configure the bundler for a browser environment. |
| WebSocket connection fails | The endpoint is absent, malformed, unreachable, or the remote browser is not listening | Verify the exact WebSocket URL and that the browser service is running with its debugging connection available to the client. |
| Connection works locally but fails from the deployed page | The deployed browser cannot reach the endpoint, or the endpoint’s network access differs from local development | Check reachability from the page’s environment and the remote service’s connection configuration. Do not expose an unprotected debugging endpoint publicly. |
| Browser connects but an operation fails | The operation may not be supported by the selected browser/protocol combination, or the remote session state differs from expectations | Check current Puppeteer browser/protocol support and inspect pages and session state after connecting. |
| Unexpected browser version or missing browser after install | Install scripts may have been blocked, or an incompatible browser was selected | Follow the installation guide’s browser install steps and check the supported-browser mapping. |
| Bundle is unnecessarily large | Unused protocol support may be included | If you do not need WebDriver BiDi, follow the guide’s note about excluding its mapper. |
6. Performance, reliability, and cost considerations
- Bundle and page startup: Bundling is required, and including an unused protocol mapper can increase the bundle. The official guide identifies mapper exclusion as an option; it does not publish bundle-size or speed benchmarks.
- Remote connection reliability: The page depends on a running browser and a working WebSocket path. Treat connection setup and remote availability as dependencies in your application, and handle connection errors at the call site.
- Browser compatibility: Match Puppeteer to a supported browser release and protocol. Arbitrary browser upgrades can break protocol expectations.
- Cost: The cited Puppeteer setup documentation does not specify remote-browser hosting prices. Any costs for the separate browser process or service depend on how you operate it.
- Access: A browser-side client makes automation calls from page JavaScript, so the endpoint must be reachable from that page. Avoid exposing an unprotected debugging endpoint.
7. When a screenshot API is simpler
If the goal is to obtain a page screenshot rather than control an interactive remote browser from your own web page, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call API returns an image or PDF and avoids setting up the browser-side Puppeteer bundle and WebSocket client.
Or skip the browser setup
Use the API directly for a screenshot:
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}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
See the ScreenshotNeo API docs for options and response details. Cookie banners are accepted and removed before capture, along with known newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server offers take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Can I launch Chromium with browser-side Puppeteer?
No. Browser-side Puppeteer connects to a separate browser that is already running. Launching or downloading a browser relies on Node.js APIs.
Does the Puppeteer client have to run on the same machine as the browser?
No, but the page needs a valid, reachable WebSocket endpoint for the browser. The reviewed guide does not specify a hosting provider or deployment recipe.
Can I use Firefox?
The current Puppeteer FAQ describes Firefox support from v23.0.0 onward and WebDriver BiDi as its default protocol. Check the current FAQ and version mapping for the release you use.
Should I use browser-side Puppeteer just to take screenshots?
Use it when the page itself needs to control an existing browser session. For a screenshot-only workflow, a screenshot API can avoid bundling the client and managing a remote browser connection.


