Chat launchers, "We're hiring" bars, newsletter pop-ups and floating feedback tabs are useful on a live site and noise in a screenshot. They sit on top of the page, usually in a corner, and the fix is always the same: hide them before the capture. This page shows how, with the selectors that worked on four real sites, what ad blocking does not do, and the mistake that turned one of our captures into a white rectangle.
How we tested. On 2026-11-08 we captured the home pages of intercom.com, hubspot.com, zendesk.com and crisp.chat at 1280×800 through our screenshot API: plain, with ad and tracker blocking, and with hide_selectors. We found the selectors with Chrome DevTools in a separate browser session.
Blocking ads does not remove chat widgets
| Site | Chat widget on the page | With block_ads + block_trackers |
|---|---|---|
| intercom.com | launcher, bottom right | still there |
| hubspot.com | launcher and a greeting bubble | still there |
| zendesk.com | launcher, bottom right | still there |
| crisp.chat | none appeared in our captures | — |
Filter lists such as EasyList target ads and trackers; a site's own chat is neither. Cookie banners are a separate case with their own option (see cookie banners in screenshots). Everything else is hidden by selector.
Selectors that worked
curl -X POST "https://shareglint.com/v1/screenshots?wait=true" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://www.hubspot.com/", "block_cookie_banners": true,
"hide_selectors": ["#hubspot-messages-iframe-container"]}'
| Widget | Selector | Result |
|---|---|---|
| Intercom | #intercom-container (plus .intercom-lightweight-app for the light launcher) |
launcher gone |
| HubSpot chat | #hubspot-messages-iframe-container |
launcher and greeting gone |
| Zendesk's own site | button[aria-label="Open chat"] |
launcher gone |
The first two are the containers these vendors' standard widgets put into a page, so they usually work on other sites that use the same chat. The Zendesk one is that site's own button; on another site, look for the container its chat puts in the page. When you capture many unknown sites, a list of the common containers costs nothing on pages that lack them:
"hide_selectors": ["#intercom-container", ".intercom-lightweight-app",
"#hubspot-messages-iframe-container", "#crisp-chatbox", "iframe#launcher"]
hide_selectors takes up to 20 selectors. Each becomes its own display: none !important rule in a stylesheet added to the page, so an invalid selector cancels only itself, and the rule also hides elements that appear after it was added — chat widgets usually load a few seconds late.
The selector that blanked the page
Our first try on Zendesk was to hide "the box that holds the button", to catch anything drawn around it: div:has(> button[aria-label="Open chat"]). The capture came back completely white. In DevTools the button's parent turned out to be the page's main wrapper, a div 1265 px wide and 12,202 px tall, with the fixed-position button as one of its nine children. The selector was right about the structure and hid the whole site.
Two habits avoid this:
- Hide the widget, not its neighbourhood. Prefer the element with the widget's own id,
aria-labelortitle; be careful with:has(), parent selectors and bare tag names (iframehides every embedded video and map too). - Test the selector first. In DevTools,
document.querySelectorAll('your selector')in the console lists what it matches; hover the result to see each element's box on the page.
Clicking instead of hiding
Some overlays are better closed than hidden: a modal that dims the page behind it, a "choose your country" dialog, a promo bar with a close button. Hiding the dialog leaves the dimmed backdrop; clicking its close button lets the site remove both. click_selector takes a selector, or a list clicked in order, before the capture:
"click_selector": ["button[aria-label=\"Close\"]"]
A click that finds nothing fails the capture with the selector named, rather than returning a picture you did not ask for, so use it on sites you know.
In your own Puppeteer code
The same technique, without our API:
await page.goto(url, { waitUntil: 'load' });
await page.addStyleTag({ content: `
#intercom-container, .intercom-lightweight-app,
#hubspot-messages-iframe-container { display: none !important; }
` });
await page.screenshot({ path: 'shot.png' });
Add the style after the page has loaded but before the screenshot. Writing all selectors into one rule, as above, has a catch: one invalid selector makes the browser drop the whole rule (MDN: selector lists), so keep one rule per selector if the list comes from users. ## When the site is yours
For your own pages it is simpler still: tell the chat not to show itself on the URL you capture. Intercom's messenger settings have hide_default_launcher ("Hide the default launcher icon", Intercom docs); HubSpot's chat can be kept from loading with window.hsConversationsSettings = { loadImmediately: false } (HubSpot docs). Read a parameter such as ?screenshot=1 in your page and set them only then, so visitors keep their chat and the screenshot service gets a clean page. The same parameter can switch off a promo bar, a cookie banner you control, or an A/B test that would make two captures of the same page differ.
Cost
Hiding and clicking add nothing to the price: a viewport capture is 1 render of the quota, a full page 2, and the same options within 24 hours are served from cache (pricing). All options are in the screenshot API reference; for clean thumbnails in bulk, see website thumbnails.