How to Hide an Element Before Taking a Puppeteer Screenshot
Hide banners, dialogs, or other page elements before a Puppeteer screenshot. Compare CSS, DOM removal, asynchronous waits, and capture options.
To hide an element before a Puppeteer screenshot, inject a temporary CSS rule or change/remove the DOM node, await that operation, then call page.screenshot(). Use display: none when surrounding content should move into the space, or visibility: hidden when the element’s space should remain. Puppeteer provides both page-context evaluation and style injection for this setup. See the Page API and screenshot guide.
1. Minimal runnable example
This complete Node.js example opens a page, hides a cookie banner, and saves a viewport screenshot. Replace the selector and URL for your page.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900 });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.addStyleTag({
content: `
.cookie-banner,
#promo-modal {
display: none !important;
}
`,
});
await page.screenshot({ path: 'page.png' });
} finally {
await browser.close();
}
Install Puppeteer in a Node project with npm install puppeteer. This example uses ECMAScript modules; save it as .mjs or configure the project for modules. The official Puppeteer screenshots guide and Page API document the capture and style-injection methods.
2. Choose how to hide the element
Inject a temporary CSS rule
page.addStyleTag() adds a style element to the page. It is a good default when you want to hide one or more matching elements without changing the site’s source files.
await page.addStyleTag({
content: '.cookie-banner { display: none !important; }',
});
await page.screenshot({ path: 'page.png' });
Use a narrow selector. A broad selector such as div can hide unrelated content. The !important declaration helps override ordinary site rules, but a site that repeatedly changes inline styles or recreates the element may need a different timing strategy.
Change or remove the node with page.evaluate()
page.evaluate() runs a function in the page context. The call is awaited, so the DOM change finishes before the screenshot starts.
await page.evaluate(() => {
const element = document.querySelector('.cookie-banner');
element?.remove();
});
await page.screenshot({ path: 'page.png' });
To preserve the node but hide it:
await page.evaluate(() => {
const element = document.querySelector('.cookie-banner');
if (element) element.style.display = 'none';
});
Removal and display: none both remove the element from layout. This can cause content below it to move upward. For a temporary browser-side change, the page’s original server response is not modified.
Use visibility when layout must remain stable
await page.addStyleTag({
content: '.cookie-banner { visibility: hidden !important; }',
});
visibility: hidden hides the element while retaining its layout box. It is useful when preserving the page geometry matters. Opacity alone is usually a poor substitute: a transparent element can still occupy space and affect interaction or compositing.
| Method | Visible? | Layout space | Use when |
|---|---|---|---|
display: none |
No | Removed | The page should close the gap |
visibility: hidden |
No | Preserved | Keep surrounding geometry stable |
element.remove() |
No | Removed | The node itself should not be in the captured DOM |
opacity: 0 |
Transparent | Usually preserved | Only when transparency, rather than suppression, is intended |
3. Handle elements that load asynchronously
If a banner is inserted after navigation, wait for it to appear before changing it. A hidden selector wait can then make the expected state explicit.
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('.cookie-banner', { timeout: 10_000 });
await page.addStyleTag({
content: '.cookie-banner { display: none !important; }',
});
await page.waitForSelector('.cookie-banner', { hidden: true });
await page.screenshot({ path: 'page.png' });
Puppeteer considers a selector hidden if it is absent or has display: none or visibility: hidden. Therefore the hidden wait can resolve immediately if the element is absent by design. Use the initial visible wait only when the element is expected and its absence should be treated as a separate condition. See the Page API for selector-wait methods.
When a site recreates the node, a one-time removal may not last. A matching style rule continues to apply to newly inserted elements, provided they use the same selector. If the application changes the element’s class or overrides styles, apply the rule after the update or remove the node immediately before capture.
4. Pick the screenshot region and format
Hide the element first; screenshot options control what region is captured, not whether an element is hidden. The ScreenshotOptions API documents options including path, type, fullPage, clip, and omitBackground.
// Full document, including content below the viewport
await page.screenshot({ path: 'full-page.png', fullPage: true });
// A rectangular region in CSS pixels
await page.screenshot({
path: 'region.png',
clip: { x: 0, y: 0, width: 900, height: 600 },
});
// Select an image format explicitly
await page.screenshot({ path: 'page.webp', type: 'webp' });
Use fullPage for the document beyond the current viewport, or clip for a specific rectangle. If the goal is to capture only a particular element, Puppeteer’s screenshot guide documents ElementHandle.screenshot(). If both fullPage and clip are supplied, choose the behavior deliberately and consult the installed version’s API because options and supported combinations can vary by version.
5. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The element still appears | The selector misses it, the hide operation was not awaited, or page code restyled/recreated it. | Check the selector in the page context, await addStyleTag/evaluate, and use a persistent matching rule or apply the change just before capture. |
| Other content disappears | The selector is too broad or matches multiple nodes. | Use a more specific class, ID, or compound selector; inspect how many nodes match before applying the rule. |
| The page content jumps | display: none or removal collapsed the element’s layout space. |
Use visibility: hidden if preserving the layout box is required. |
| The wait reports a timeout | The selector never appeared, differs on this route, or is inside another frame. | Check the actual selector and navigation state; handle optional banners separately. For an iframe, operate on that frame’s page context. |
| The hidden wait returns immediately | The selector is absent, which counts as hidden. | First wait for the element to appear if its presence is required, then hide it and wait for the hidden state. |
| Injected style does not take effect | Site styles or scripts override it, or the style was added to a different page/frame. | Use a specific selector with !important, target the frame containing the element, or remove the node after it appears. |
| The screenshot is clipped or unexpectedly large | The selected viewport, fullPage, or clip does not match the desired output. |
Set the viewport before navigation and choose one capture region intentionally. |
6. Performance, reliability, and cost
Injecting one style rule or changing one DOM node is usually a small preparation step compared with navigating and rendering a page. Avoid polling with repeated arbitrary delays when a selector wait can express the state you need. A fixed delay may be appropriate for a known animation or delayed render, but it adds time and does not prove the element is ready.
For repeatable captures, keep the selector specific, set a known viewport, wait for the relevant page state, and use a consistent screenshot option set. If pages vary, log the URL and whether the target selector was found so a missing banner is distinguishable from a hide failure. Puppeteer’s official guide displayed version 25.12.0 when researched; check the documentation matching your project’s pinned package when using older versions.
Self-hosting means your runtime is responsible for browser startup, page navigation, rendering, storage, and retries. Factor those resources and maintenance into your operating cost. The page-level hide itself does not require a third-party screenshot service.
7. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server by Yorker Media. A single request captures a URL as PNG, JPEG, WebP, or PDF; the API parameter names used by other screenshot APIs also work, which can make switching easier. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);
ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the page verdict and billing status in headers. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is available on every plan. Create a free ScreenshotNeo account.
8. FAQ
Does hiding the element change the live website?
No. The examples change the DOM or styles in the browser page Puppeteer is controlling; they do not edit the site’s source files.
Can I hide an element inside an iframe?
Yes, but the selector must run in the frame that owns the element. Identify the frame, then evaluate or inject the style in that frame’s context.
Can I capture only the element I want to keep?
Yes. Use an element handle’s screenshot method for element-level capture, as described in the Puppeteer screenshot guide.
Does hiding a banner remove its network requests?
No. CSS hiding and DOM removal affect the rendered page; they do not by themselves prevent the page from making requests.


