Remove a Virtual Screen in Puppeteer
Remove a headless Chrome screen with Puppeteer’s `browser.removeScreen(id)`. Learn how to tell a screen from a page viewport, inspect screen state, and fix common issues.
If you added a virtual screen to a running headless Chrome browser with Puppeteer, remove it by passing the screen’s ID to browser.removeScreen(id). Use the ID returned by browser.addScreen():
const screen = await browser.addScreen({
left: 800,
top: 0,
width: 800,
height: 600,
label: 'second screen',
});
await browser.removeScreen(screen.id);
This changes Chrome’s screen configuration. If you mean that a page has the wrong dimensions, you probably want to reset its viewport with page.setViewport(null) instead. A viewport is page-level emulation; it is not a screen in the browser’s screen list.
Identify what you need to remove
| What you see | What it is | Use |
|---|---|---|
| An extra screen added while headless Chrome is running | Browser screen configuration | browser.removeScreen(screenId) |
| A page rendered at an unexpected desktop or mobile size | Page viewport | page.setViewport(null), or set an explicit viewport |
| The browser content area needs a particular width and height | Browser window/content sizing | page.resize({ contentWidth, contentHeight }) |
Puppeteer documents runtime screen addition and removal for headless Chrome. browser.screens() can inspect the screen list in both headless and headful modes, but dynamic add/remove is headless-only. See the official screen configuration guide.
Remove an added screen
Keep the object returned by addScreen() and use its id. Here is a complete example using puppeteer-core and an installed Chrome executable:
const puppeteer = require('puppeteer-core');
async function main() {
const browser = await puppeteer.launch({
executablePath: process.env.CHROME_PATH || '/path/to/chrome',
headless: true,
});
try {
const before = await browser.screens();
console.log('Screens before:', before);
const addedScreen = await browser.addScreen({
left: 800,
top: 0,
width: 800,
height: 600,
label: 'second screen',
});
console.log('Added screen:', addedScreen);
console.log('Screens after add:', await browser.screens());
await browser.removeScreen(addedScreen.id);
console.log('Screens after remove:', await browser.screens());
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Install puppeteer-core in your project and set CHROME_PATH to the Chrome or Chromium executable available in your environment. Puppeteer’s screen examples use puppeteer-core; the guide shows the screen count returning to one after a second screen is removed.
When you already have the screen ID
If your code stored the ID earlier, pass it directly. You do not need to recreate the screen object:
await browser.removeScreen(savedScreenId);
Do not pass a viewport object or the entire screen object. The method takes the screen ID.
Inspect screens before removing one
Call browser.screens() to see the current screen configuration. This helps distinguish an extra browser screen from a page viewport problem, and lets you confirm the state after removal:
const screens = await browser.screens();
console.log(screens);
Use the identifier associated with the screen you intend to remove. Avoid assuming a particular generated ID or that the ID belongs to a specific page.
Reset a page viewport instead
If no screen was added and the page simply has a constrained viewport, restore Puppeteer’s default viewport with:
await page.setViewport(null);
null resets the page viewport to its default value. It does not remove a browser screen. If you need a known size rather than the default, set it explicitly:
await page.setViewport({ width: 1280, height: 800 });
Viewport changes involving isMobile or hasTouch can reload the page in some cases, according to the Page.setViewport API reference. Account for that reload if your script depends on page state or event timing.
Resize browser content rather than changing screens
If your goal is to control the browser content area’s dimensions, Puppeteer documents Page.resize() as a separate operation. Clear the default viewport first, then wait for the window resize event because the inner window dimensions update asynchronously:
await page.setViewport(null);
await Promise.all([
page.evaluate(() => new Promise((resolve) => {
window.addEventListener('resize', resolve, { once: true });
})),
page.resize({ contentWidth: 1200, contentHeight: 900 }),
]);
const size = await page.evaluate(() => ({
width: window.innerWidth,
height: window.innerHeight,
}));
console.log(size);
Follow the current Puppeteer window management guide for the API’s supported behavior in your version. This is for content/window sizing, not for removing a headless screen.
Headless screen configuration and launch options
The screen-removal method applies to a screen added at runtime in headless mode. It does not remove a physical display from a headful Chrome session. In headful mode, Chrome uses the platform’s actual screen configuration.
The initial headless screen configuration also depends on launch arguments. According to the screen guide, without --screen-info, headless Chrome starts with one 800×600 screen by default. If --window-size is specified, that screen is as large as the requested window. For example:
const browser = await puppeteer.launch({
headless: true,
args: ['--window-size=1440,900'],
});
These launch dimensions affect the initial setup; they are not a substitute for removing a screen already added through addScreen(). Check the screen list at runtime when the browser is already running.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
browser.removeScreen is not a function |
The installed Puppeteer version does not expose this API, or the browser is not running in the supported headless configuration. | Check the installed Puppeteer version and the current screen configuration guide. Confirm you launched Chrome headless. |
| Removal does not affect the page dimensions | The issue is the page viewport, not the browser screen list. | Use await page.setViewport(null) to reset the viewport, or set the viewport size you need. |
| The page still behaves as if it has an extra display | The wrong screen ID may have been passed, or the screen list was not checked after the operation. | Inspect await browser.screens(), remove the ID of the added screen, then inspect the list again. |
| The screen API fails in a visible Chrome session | Runtime screen addition/removal is documented for headless mode only. | Run the automation headless if runtime virtual-screen changes are required. Headful Chrome uses platform screens. |
| The page reloads after changing viewport options | Changing isMobile or hasTouch can trigger a reload in some cases. |
Wait for navigation or reload as appropriate, then re-establish page state. |
| Measured inner width/height is stale after resize | Window size updates asynchronously. | Wait for the resize event after calling page.resize(), as in the Puppeteer window management guide. |
Performance, reliability, and cost
- Performance: Removing a screen changes browser configuration; it does not itself capture a screenshot or make a slow page load faster. If you are resizing content, wait only for the resize event you need rather than using an arbitrary long delay.
- Reliability: Save the ID returned by
addScreen()and inspectscreens()when diagnosing state. Keep cleanup in afinallyblock when a browser may be reused, so later work does not inherit an unintended screen. - Cost: The Puppeteer screen configuration operation is a local browser automation API call; the cited documentation specifies no per-screen charge. Your compute or hosted-browser costs depend on where Chrome runs.
Or skip the browser setup
If your actual goal is to get a screenshot of a page, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF, without setting up a Puppeteer browser for this capture:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up free and get 1,000 screenshots a month with no card.
FAQ
Can I remove the default headless screen?
The documented removal flow is for a screen added at runtime: pass its returned ID to browser.removeScreen(). To change initial dimensions, review the headless launch configuration such as --window-size.
Does removing a screen close a page?
The screen API changes the browser’s screen configuration. It is distinct from closing a page or resetting that page’s viewport.
Can I remove a monitor from headful Chrome with Puppeteer?
The documented dynamic screen removal API is headless-only. In headful mode, inspect available screens with browser.screens(); Puppeteer does not document runtime removal of the platform’s physical displays through this API.
Which API should I use for responsive testing?
Set the page viewport and relevant mobile or touch options for responsive layout testing. Use screen configuration only when the test specifically depends on multiple browser screens.


