Hide the element before you call page.screenshot(). In Puppeteer, the dependable sequence is to inject a temporary CSS rule with page.addStyleTag(), or remove/change the node with page.evaluate(), await that operation, optionally verify the hidden state, and then capture the page. This prevents cookie banners, sticky headers, dialogs, and other visual distractions from appearing in the image.
1. The basic pattern
A screenshot records the page as it is rendered at capture time. Puppeteer does not have a screenshot option that means “ignore this selector”; you must change the page first. The official Page API provides both addStyleTag and evaluate for that preparation, while Page.screenshot() performs the capture.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.addStyleTag({
content: `
.cookie-banner,
#promo-modal {
display: none !important;
}
`,
});
await page.screenshot({ path: 'page.png' });
await browser.close();
The selectors must identify only the unwanted node. The !important flag usually wins over the site’s normal stylesheet, but an inline !important declaration or a script that continually rewrites the element may require removal or a later application of the rule.
2. Hide with an injected CSS rule
Use display: none when the gap should disappear
display: none removes the element from layout. Content below a banner moves up and a modal no longer occupies space. This is generally the right choice for a clean document screenshot.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →await page.addStyleTag({
content: `
.cookie-banner { display: none !important; }
[data-testid="newsletter-popup"] { display: none !important; }
.chat-widget { display: none !important; }
`,
});
await page.screenshot({ path: 'clean.png', fullPage: true });
Injecting one rule is useful when the target is created after navigation: the selector remains active and applies as soon as the matching node appears.
Use visibility: hidden when geometry must remain stable
visibility: hidden makes the element invisible but preserves its layout box. Choose it when you need surrounding coordinates, spacing, or sticky positioning to remain unchanged.
await page.addStyleTag({
content: '.sticky-header { visibility: hidden !important; }',
});
Unlike opacity, visibility is recognized by Puppeteer’s hidden-state checks. An element with opacity: 0 can still occupy space and affect compositing, so opacity alone is a poor way to suppress a visual obstruction.
#1 Best Overall
3. Remove or modify the node with page.evaluate
Use page-context JavaScript when you want the node gone, need to inspect it, or must override an inline style.
await page.evaluate(() => {
const element = document.querySelector('.cookie-banner');
element?.remove();
});
await page.screenshot({ path: 'without-banner.png' });
If other code needs the node to remain in the DOM, change its style instead:
await page.evaluate(() => {
const element = document.querySelector('#promo-modal');
if (element) element.style.setProperty('display', 'none', 'important');
});
Removing a node also removes its layout space. If the site recreates it, a one-time removal may not last; inject a persistent selector rule or remove it immediately before the screenshot.
4. Wait for asynchronous pages
Single-page applications often insert consent dialogs and promotional components after the initial HTML arrives. Apply the style before insertion when possible, then verify the result.
await page.addStyleTag({
content: '.cookie-banner { display: none !important; }',
});
await page.waitForSelector('.cookie-banner', { hidden: true });
await page.screenshot({ path: 'verified.png' });
Puppeteer treats a selector as hidden when it is absent, has display: none, or has visibility: hidden. Therefore this wait also resolves when the application never renders the banner. It is useful as an explicit state check, not as a requirement for every capture.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →When the element appears later
If you cannot install the rule before navigation, wait for the node, hide it, and capture:
await page.waitForSelector('.cookie-banner', { visible: true });
await page.evaluate(() => {
document.querySelector('.cookie-banner')?.remove();
});
await page.screenshot({ path: 'late-banner-removed.png' });
For a component that may be recreated between these calls, prefer addStyleTag and then use waitForSelector(..., { hidden: true }).
5. Choose the correct selector
- Prefer stable hooks: IDs, dedicated classes, or attributes such as
data-testidare less fragile than generated class names. - Scope broad selectors:
.bannermight match several legitimate notices; inspect the DOM and target the exact component. - Account for iframes: a selector in the main document cannot reach an element inside an iframe. Obtain the frame with
page.frames(), wait in that frame, and run the hide operation there. - Shadow DOM requires page-context access: query the host and its
shadowRoot, then alter the shadow element, or use a style mechanism supported by that component. - Check the rendered result: a matching selector can still leave a backdrop, focus trap, or fixed pseudo-element visible. Hide the backdrop separately when necessary.
await page.evaluate(() => {
const host = document.querySelector('consent-dialog');
host?.shadowRoot?.querySelector('.backdrop')?.remove();
});
6. Capture the right region
After the hide operation, choose the screenshot scope documented in Puppeteer’s Screenshots guide and ScreenshotOptions interface.
| Goal | Option | Effect |
|---|---|---|
| Visible viewport only | No fullPage or fullPage: false |
Captures the current viewport. |
| Entire document | fullPage: true |
Captures the full scrollable page after the element is hidden. |
| One rectangle | clip: { x, y, width, height } |
Captures only the specified viewport region. |
| One element | elementHandle.screenshot() |
Captures the element’s bounding region, as described in the guide. |
const card = await page.$('#pricing-card');
if (!card) throw new Error('pricing card not found');
await card.screenshot({ path: 'card.png' });
Hiding a child and capturing its parent can produce cleaner framing than a full-page image when the unwanted content is inside a component.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows 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 reinstallRank #3
7. A complete reusable helper
import puppeteer from 'puppeteer';
async function screenshotWithout(page, selectors, options = {}) {
const css = selectors.map((selector) =>
`${selector} { display: none !important; }`
).join('n');
await page.addStyleTag({ content: css });
for (const selector of selectors) {
await page.waitForSelector(selector, { hidden: true });
}
return page.screenshot({ path: 'output.png', ...options });
}
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await screenshotWithout(page, ['.cookie-banner', '.chat-widget'], { fullPage: true });
await browser.close();
The helper deliberately waits after installing the rule. If a selector is optional and may never exist, the hidden wait resolves because absence counts as hidden. For strict validation, first use page.waitForSelector(selector) and handle a timeout as an expected “not present” case.
8. Troubleshooting
The element is still visible
- Log or inspect
document.querySelector(selector)to confirm the selector matches the intended node. - Try
!important, or remove the node withevaluatewhen inline styles win. - Check for a separate backdrop, pseudo-element, iframe, or shadow-root copy.
- Apply the rule after navigation and after any route change that replaces the document.
The page layout has an unwanted gap
Switch from visibility: hidden to display: none, or remove the node. The former preserves the layout box by design.
The banner returns before capture
A framework may recreate it or rewrite its style. Keep a matching injected rule active, wait for the hidden state immediately before screenshot, and avoid long asynchronous work between hiding and capture.
waitForSelector times out
With { hidden: true }, absence should resolve. A timeout commonly means the selector is wrong, the element is in a different frame, or it remains visible because the rule did not match. Verify the frame and computed state.
Free tools Windows power users keep installed
One-click scans. No signup required.
Full-page output differs from the viewport
Lazy images, sticky elements, and scroll-triggered scripts can change during a full-page capture. Wait for the page’s content, hide fixed overlays, and use a clipped or element screenshot when you need a stable region.
The screenshot fails or is blank
Await every navigation, style injection, evaluation, and selector wait. Confirm the page loaded and that the capture path is writable. A hidden element operation does not repair navigation, authentication, or resource failures.
9. Reliability, performance, and version notes
- Injecting one small style tag is usually cheaper and less disruptive than repeatedly querying and removing nodes.
- Use
waitUntilappropriate to the site;networkidle2is not a guarantee that application rendering has finished. - Keep selectors and CSS in the capture code so the result is reproducible across runs.
- Pin and test against the Puppeteer version installed by your project. The official guide displayed version 25.12.0 when this article was prepared; APIs can differ in older releases.
- Restore the original page only if later steps in the same browser session need it. Otherwise close the page or browser after capture.
Or skip the browser setup
If you need an API rather than a managed Puppeteer process, ScreenshotNeo accepts one GET request and returns a PNG, JPEG, WebP, or PDF. It removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing state.
Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The API also supports full-page and element captures, dark mode, device presets, custom viewports and retina scale, PDF controls, custom CSS and JavaScript, clicks, waits, blocking rules, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Parameter names used by other screenshot APIs also work.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
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 documentation for option names and authentication. Equivalent clients:
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to start with the monthly free allowance.
10. Practical decision checklist
- Need the page’s own browser state, authentication, or custom test flow? Use Puppeteer and hide the node with CSS or
evaluate. - Need layout to collapse? Choose
display: noneor removal. - Need coordinates preserved? Choose
visibility: hidden. - Does the component load asynchronously? Inject a persistent rule and verify with
waitForSelector(..., { hidden: true }). - Need only one component? Use
ElementHandle.screenshot()orclip. - Need repeatable remote captures without maintaining Chromium? Use ScreenshotNeo’s API or MCP server.
Frequently Asked Questions
Does hiding an element change the website for other visitors?
No. The CSS rule, style change, or removal occurs only inside the Puppeteer-controlled page instance and is not sent back to the site’s server.
Can I hide an element without changing layout?
Yes. Apply visibility: hidden !important instead of display: none; the element’s layout box remains.
Should I remove a cookie banner or only hide it?
Hide it when you need a reversible capture and remove it when the DOM itself must no longer contain the component. A persistent style rule is safer if scripts recreate the banner.
Can Puppeteer hide an element inside an iframe?
Only after selecting the iframe’s Frame object and running the wait and page-context operation in that frame; main-page selectors do not cross frame boundaries.
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.




