How to Capture a Screenshot of a Web Page with Open Shadow DOM Menus
Open the menu before capturing it. Use Playwright for a viewport screenshot or a close element crop, with practical guidance for targeting, timing, and troubleshooting.
To capture a web page with an open Shadow DOM menu, first open the menu in the browser, wait for it to become visible, and then take a screenshot. In Playwright, use page.screenshot() to include page context or a locator’s screenshot() method to crop to the menu. A screenshot API captures the state currently rendered in the page; it does not open the menu for you.
Shadow DOM changes how a component’s internals are organized, but the practical task is the same: perform the user interaction, confirm the menu is visible, then capture the page or the element. Exact selectors and menu behavior depend on the site. The examples below use accessible roles where available, and should be adapted to the target page.
1. Choose a page screenshot or a menu crop
| Capture | Use it when | Trade-off |
|---|---|---|
| Viewport screenshot | You need the open menu in context with the surrounding page. | Does not require a selector for the menu, but includes other visible content. |
| Full-page screenshot | You need the page’s full scrollable content. | A transient menu may only be visible in the current viewport; a full-page image may not show it as expected. |
| Element screenshot | You need a close crop of the menu. | Requires a locator that matches the rendered menu and remains attached while captured. |
Playwright’s locator screenshot scrolls the target into view and runs actionability checks. If the element is detached while the screenshot is being taken, the operation can fail. A covering element can also affect what appears in the resulting image. See the official Playwright Page API and Locator API.
2. Capture an open menu with Playwright
This runnable JavaScript example starts Chromium, navigates to a page, clicks a menu button, waits for a menu to be visible, and saves both a viewport screenshot and a close crop. Install Playwright and its browser first:
npm install playwright
npx playwright install chromium
Save the following as capture-menu.mjs. Replace the example URL and accessible names with those from the page you are capturing.
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
try {
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
// Use the accessible name exposed by the actual page.
await page.getByRole('button', { name: 'Open menu' }).click();
const menu = page.getByRole('menu');
await menu.waitFor({ state: 'visible' });
// Include the menu and surrounding page context.
await page.screenshot({ path: 'page-with-open-menu.png' });
// Save a close crop of the menu itself.
await menu.screenshot({ path: 'open-menu.png' });
} finally {
await browser.close();
}
Run it with node capture-menu.mjs. The code uses semantic locators, but a component may expose different roles or names. If the page does not expose a menu role, inspect the rendered page and choose a locator that matches its actual markup. A menu can be rendered in a portal or use custom accessibility markup, so there is no universal selector for every site.
Wait for the right condition
Prefer waiting for the menu to become visible over adding an arbitrary sleep. If the menu animates into place, you may need to wait for the transition to finish before capture. For a controlled visual test, Playwright supports screenshot settings such as disabling animations; only apply styling or animation changes when changing the rendered page for the capture is acceptable. Its screenshot style option pierces Shadow DOM and applies to inner frames, which can help normalize a screenshot or hide dynamic content. It does not open the menu.
For example, to take a screenshot after the menu opens while disabling animations and applying a small stylesheet:
await page.getByRole('button', { name: 'Open menu' }).click();
await page.getByRole('menu').waitFor({ state: 'visible' });
await page.screenshot({
path: 'stable-menu.png',
animations: 'disabled',
style: '* { caret-color: transparent !important; }'
});
Consult the current Page API documentation for the available screenshot options in your installed Playwright version.
3. Inspect Shadow DOM or capture with Chrome DevTools Protocol
If a menu is hard to locate, inspect the page’s composed structure and adapt the locator to what is actually rendered. Chrome DevTools Protocol (CDP) provides a lower-level option for workflows that already use browser debugging commands:
Page.captureScreenshotcaptures an image and supports a clip region and thecaptureBeyondViewportparameter.DOMSnapshotcan return a flattened DOM tree that includes Shadow DOM. It helps inspect structure; it does not create a screenshot.
These are separate operations: inspect structure with DOMSnapshot if needed, then capture the visible page or a clip with Page.captureScreenshot. See the official CDP Page domain and DOMSnapshot domain documentation for protocol details.
4. Troubleshoot missing or incomplete menus
| Symptom | Likely cause | What to check or change |
|---|---|---|
| The screenshot has no menu. | The click did not open it, or capture began before it appeared. | Trigger the menu through the page’s normal interaction and wait for the menu to be visible before capturing. |
| The locator cannot find the menu. | The page uses a different role, accessible name, portal, or custom markup. | Inspect the rendered page and adapt the locator. Do not assume every component uses role="menu". |
| The element screenshot fails because the target detached. | The component re-rendered, closed, or replaced the menu during capture. | Keep the menu open and stable, then retry. If a stable element locator is not available, capture the viewport. |
| The crop contains only part of the menu or unexpected content. | The target is clipped, covered, or changed during capture. | Check the visible menu bounds and overlays. Use a viewport screenshot when context or an element crop is unreliable. |
| The menu is visible but in an intermediate animation state. | The screenshot was taken during the transition. | Wait for the transition to finish, or disable animations in a controlled screenshot test. |
| The menu closes when capture starts. | A blur, focus change, or page-specific event closed the transient UI. | Check the page behavior around focus and capture timing; take the screenshot immediately after visibility is confirmed. |
5. Make the capture reliable and keep its cost predictable
- Wait on state, not a guessed delay. A visibility check ties capture to the menu being rendered. Use a fixed delay only when the page has a known transition or delayed behavior that cannot be observed with a better condition.
- Keep the interaction consistent. Use the same click or keyboard interaction each run, and use the same viewport when comparing screenshots.
- Choose the smallest useful image. Use an element screenshot for a focused menu image and a viewport screenshot when page context matters. Full-page captures can be larger and may not represent transient content as intended.
- Account for dynamic pages. Network activity, late content, animations, overlays, and responsive layout can change the image. Wait for the specific state relevant to the capture and inspect the saved output.
- Plan for site-specific behavior. Shadow DOM does not guarantee that a menu has a particular selector or that it stays open after focus changes. Validate the interaction and target on the page you need to capture.
Playwright is the direct choice when the job requires browser interaction to open a particular menu. For recurring captures that do not require opening a site-specific menu, a screenshot API can avoid maintaining browser setup. ScreenshotNeo is a website screenshot API and MCP server from Yorker Media; it accepts one GET request with a URL and returns PNG, JPEG, WebP, or PDF. Its API cannot perform the site-specific menu-opening interaction shown above.
Or skip the browser setup
For a page that is already in the state you need, ScreenshotNeo can capture it with one request. See the ScreenshotNeo API docs for configuration and supported parameters.
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,
)
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}`);
await Bun.write('shot.webp', res);
ScreenshotNeo accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, with no card required.
FAQ
Does screenshot styling open a Shadow DOM menu?
No. Playwright’s screenshot style option can affect styles inside Shadow DOM and inner frames, but your script still has to open the menu first.
Should I use a full-page screenshot for a menu?
Only if you need the full scrollable page. For a transient menu, a viewport capture or element crop is usually a better fit because the menu may only be visible in the current view.
Can DOMSnapshot take the screenshot?
No. It provides a flattened DOM representation that includes Shadow DOM for inspection. Use a screenshot capture method separately to create the image.
Is there a selector that works for every Shadow DOM menu?
No. The component’s markup and accessibility exposure vary by site. Inspect the rendered page and use a locator suited to that menu.


