How to Capture a Website Screenshot with a Specific Element Highlighted
Learn when to crop an element and when to mark it in a wider screenshot, with runnable Playwright code, DevTools steps, and troubleshooting tips.
To capture a website screenshot with a specific element highlighted, first decide whether you want a tight crop of the element or a screenshot of the page with the element marked. For repeatable captures, Playwright can screenshot a locator directly; to keep the surrounding page visible, take a page screenshot and add a colored mask or annotation. For a one-off manual capture, Chrome DevTools Inspect mode helps you find and select the element.
Choose the framing before you capture
These outputs serve different purposes:
| Output | Use it when | How to make it |
|---|---|---|
| Element crop | The element itself is the subject, such as a button, chart, or card. | Capture a locator or selected DOM node. |
| Viewport screenshot with a mark | You need to show where the element sits in the visible page. | Capture the viewport and add a visual outline or annotation. |
| Full-page screenshot with a mark | You need the page context beyond the current viewport. | Capture the full page, then add a mark that remains aligned with the target. |
A locator screenshot is a crop; it does not preserve the surrounding layout. If the reader needs to understand where the element appears on the page, use a page screenshot and mark the target instead.
One-off capture with Chrome DevTools
- Open the page in Chrome and open DevTools.
- Activate Inspect mode, then hover over the target. Chrome highlights the element under the pointer.
- Click the target to select its node in the Elements panel. The inspection tooltip can show details such as its selector, dimensions, colors, font, padding, and margin.
- Capture the selected node if your browser version exposes a node screenshot command. Browser UI commands can change, so use the Playwright workflow below when you need a documented, repeatable capture.
See the official Chrome DevTools Inspect mode guide for selection and inspection details.
Automate an element crop with Playwright
Playwright’s locator screenshot captures the matched element. Install Playwright and its Chromium browser, save the following as capture.mjs, and run it with Node.js. Replace the URL and selector with the page and element you need.
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
try {
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
const target = page.locator('.header');
await target.waitFor({ state: 'visible', timeout: 15000 });
await target.screenshot({ path: 'element.png' });
} finally {
await browser.close();
}
Install dependencies with npm install playwright and npx playwright install chromium. The selector .header is an example; use a stable selector from the page you control, such as a test ID. Playwright documents element, page, full-page, and buffer screenshots.
Use a full-page or viewport screenshot instead
For the visible viewport, call page.screenshot(). For the full scrollable page, pass fullPage: true. These are page captures, unlike locator.screenshot().
await page.screenshot({ path: 'viewport.png' });
await page.screenshot({ path: 'full-page.png', fullPage: true });
To mark an element while keeping the page context, add a temporary outline to the target before capturing. This example adds a red outline and removes it afterward:
const target = page.locator('.header');
await target.waitFor({ state: 'visible' });
await target.evaluate((element) => {
element.dataset.screenshotOriginalOutline = element.style.outline;
element.style.outline = '4px solid #e11d48';
element.style.outlineOffset = '3px';
});
await page.screenshot({ path: 'page-with-highlight.png', fullPage: true });
await target.evaluate((element) => {
element.style.outline = element.dataset.screenshotOriginalOutline || '';
delete element.dataset.screenshotOriginalOutline;
element.style.removeProperty('outline-offset');
});
This outline changes the page’s rendered style for the capture. If the page already has a complex outline or you cannot modify its DOM, use a separate annotation step after capturing. Playwright can also mask locator areas in a page screenshot with a colored box; a mask conceals the target rather than drawing a conventional outline, so use it when that effect is acceptable. See the Playwright Page screenshot options.
Wait for the page and target to be ready
A visible element is not always ready to capture: images may still be loading, a client-rendered page may not have populated the target, or an animation may be mid-frame. Wait for a meaningful selector or application condition. Use a short delay only when the site has a known delayed visual change.
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('.header').waitFor({ state: 'visible', timeout: 15000 });
await page.screenshot({ path: 'ready.png' });
Navigation events such as networkidle can be unsuitable for pages that keep connections open or poll continuously. Prefer waiting for the actual content you need.
Highlight overlays and annotated screenshots
For a full-page screenshot with a conspicuous callout, an outline can work when the element is in the DOM and styling it is safe. For a more controlled result, capture the page first and draw a rectangle or annotation over the target afterward. Playwright MCP documents screenshot workflows including a persistent highlight overlay and annotated screenshots; see Playwright MCP screenshots and Playwright MCP debugging tools.
When annotating after capture, keep the screenshot dimensions and page coordinates together. A viewport capture uses viewport coordinates. A full-page image may be taller than the viewport, so the element’s document position—not just its current viewport position—must determine where the mark goes. If the page has sticky headers or content that shifts during capture, verify the final mark visually.
Practical edge cases
- Multiple matches: A locator may match more than one element. Narrow the selector or use a specific locator such as
.cardwithfilteror an index only when order is stable. - Hidden or detached target: Wait for visibility and ensure the page has finished rendering the component. A selector can exist while its element is hidden.
- Overlapping content: A modal, sticky header, tooltip, or cookie dialog may cover the target. Close or account for the overlay before capture.
- Nested scrolling: A locator screenshot scrolls the target into view, but a screenshot of a scrollable container includes only the content currently in that container’s scroll position. Scroll the container itself if you need a different section. See the Playwright ElementHandle screenshot notes.
- Lazy-loaded content: Scroll the target into view and wait for its content or images before capturing. A full-page capture does not guarantee every site-specific lazy-loading script has completed.
- Animations: A screenshot can land between animation frames. Disable animations through page styling or wait for the desired state when the exact frame matters.
- Cross-origin frames: Select the frame’s content with Playwright’s frame APIs where possible; a page-level selector does not automatically search inside every frame.
- Responsive layout: Set the viewport explicitly so the target’s position and dimensions are repeatable. Use a device viewport when the mobile layout is the subject.
Or skip the browser setup
ScreenshotNeo is a website screenshot API. One GET request can return a screenshot in PNG, JPEG, or WebP, or a PDF. Its API also supports capturing an element by CSS selector and custom CSS, so you can request the element or style a target for emphasis. See the ScreenshotNeo API documentation for available 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,
)
r.raise_for_status()
with open("shot.webp", "wb") as image:
image.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 import('node:fs/promises').then(({ writeFile }) =>
writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);
Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot, and each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers identify the page verdict and billing status. An MCP server lets AI agents such as Claude and Cursor take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan.
Sign up free for 1,000 screenshots a month, with no card required.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Timeout waiting for the locator | The selector is wrong, the element is hidden, or the page has not rendered it. | Inspect the page, choose a stable selector, and wait for the application’s actual ready state. |
| Screenshot is blank or incomplete | Capture began before client rendering, fonts, images, or lazy content finished loading. | Wait for the target and relevant assets, and scroll lazy-loaded content into view. |
| The target is missing from a locator screenshot | The locator matched a different node, or the desired item is inside a frame or scroll container. | Check the match count, scope the locator, select the correct frame, or scroll the container. |
| The target is visible but obscured | A dialog, sticky element, or chat widget overlaps it. | Dismiss or hide the covering element, then recapture and inspect the result. |
| The full-page screenshot cuts off content | The page uses nested scrolling or content loads only as the user scrolls. | Scroll the relevant container and trigger lazy loading before capturing; capture that container separately if needed. |
| The outline changes layout or looks clipped | The chosen border affects dimensions, or the target sits at a clipping boundary. | Use an outline rather than a border, adjust the offset, or add the marker after capture. |
| Capture differs between runs | Viewport, fonts, animations, page data, or network timing varies. | Fix the viewport, wait on stable content, and disable or settle animations where appropriate. |
Performance, reliability, and cost
For a single target, a locator screenshot avoids producing and storing a much larger full-page image. Full-page capture can take longer and consume more memory for long pages; capture only the needed framing and image format. Reuse a browser process for batches of pages instead of starting Chromium for every screenshot, while creating an isolated page or context for each job as your privacy requirements dictate.
Make captures repeatable by fixing the viewport, waiting for a selector that represents readiness, and handling navigation and screenshot errors. Close the browser in a finally block, as in the example. For automated pipelines, retain enough context to diagnose failures: URL, selector, viewport, wait condition, and error. ScreenshotNeo offers caching with a caller-chosen TTL, bulk capture of up to 100 URLs per call, and async jobs with signed webhooks; consult its documentation for request options and usage.
With local Playwright, cost depends on the compute and storage you operate; there is no per-screenshot API charge from Playwright itself. For a hosted API, compare the plan allowance and handling of failed captures. ScreenshotNeo says only clean shots are billed, with 1,000 free per month and paid plans from $5 for 3,000; its response includes billing and page-verdict headers. Do not infer that a successful HTTP response alone means the page content was suitable—check the returned status information.
FAQ
Can I highlight an element without cropping out the page?
Yes. Take a page screenshot and add an outline or annotation to the target. A locator screenshot alone crops to the element.
Does Playwright scroll an off-screen element into view?
Element screenshot capture scrolls the target into view. A nested scroll container can still show only its current contents, so scroll that container when needed.
Can I save screenshot bytes instead of a file?
Yes. Playwright screenshot methods can return image bytes for processing or storage in your own pipeline.
Which method is best for a repeatable capture?
Use a Playwright locator with a stable selector for an element crop. Use a page screenshot plus an explicit marker when context matters.


