How to Screenshot an Element Outside the Viewport with Playwright
Use Playwright’s locator screenshot to capture an offscreen element. It scrolls the target into view; choose full-page capture when you need the whole document.
Use locator.screenshot() to capture an element outside the viewport. Playwright waits for the locator’s actionability checks, scrolls the matched element into view, and captures the element. A locator screenshot returns a buffer; pass path to save it to a file. For example:
const resultHeading = page.getByRole('heading', { name: 'Results' });
await resultHeading.screenshot({ path: 'results-heading.png' });
This captures the selected element, not the entire page. Use page.screenshot({ fullPage: true }) when you need the full scrollable document. [Playwright Locator API] [Playwright screenshot guide]
1. Set up a runnable Playwright example
The following Node.js script launches Chromium, opens a page, locates a heading that starts below the initial viewport, and saves the heading screenshot. Install Playwright and its browser first:
npm init -y
npm install playwright
npx playwright install chromium
Save this as screenshot-element.js and run it with node screenshot-element.js:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
await page.setContent(`
<main>
<div style="height: 1400px">Content above the target</div>
<h2>Results</h2>
</main>
`);
const target = page.getByRole('heading', { name: 'Results' });
await target.screenshot({ path: 'results-heading.png' });
} finally {
await browser.close();
}
})();
In an existing Playwright test or script, the essential operation is just creating a locator for the target and calling await locator.screenshot({ path: 'element.png' }). A path is optional: without it, the method returns a screenshot buffer you can pass to another function or write yourself. [Locator screenshot API]
2. Choose a locator that survives page changes
Prefer a locator that identifies the element by its user-facing meaning or an explicit testing contract. Playwright recommends built-in locators such as getByRole(), getByText(), getByLabel(), and getByTestId(). Locators resolve against the current page when used, which helps when a framework replaces DOM nodes during rendering. [Playwright locator guide]
// Accessible role and name
await page.getByRole('heading', { name: 'Results' })
.screenshot({ path: 'heading.png' });
// Visible text
await page.getByText('Quarterly revenue', { exact: true })
.screenshot({ path: 'revenue.png' });
// Explicit test contract
await page.getByTestId('chart-panel')
.screenshot({ path: 'chart.png' });
// CSS is supported when needed
await page.locator('#results-panel')
.screenshot({ path: 'panel.png' });
If the locator matches more than one element, make it specific enough to identify one target—for example, scope it to a section or use an accessible name. Avoid long CSS or XPath chains tied to incidental DOM structure; those selectors tend to break when markup changes. [Locator guidance]
3. Select the right capture scope
| What you need | Use | What the image contains |
|---|---|---|
| One offscreen element | locator.screenshot() |
The page clipped to the matched element’s size and position. Playwright scrolls the target into view first. |
| The full document | page.screenshot({ fullPage: true }) |
The full scrollable page, rendered as a tall screenshot. |
| A particular part of a scrollable panel | Scroll the panel to the desired position, then screenshot the panel locator | Only the scrollable element’s currently displayed interior; it is not stitched into a capture of all its inner content. |
// One element, even when initially offscreen
await page.getByTestId('summary-card')
.screenshot({ path: 'summary-card.png' });
// Entire scrollable document
await page.screenshot({ path: 'full-page.png', fullPage: true });
// Current view of a scrollable container
const panel = page.getByTestId('results-scroll-area');
await panel.evaluate(element => { element.scrollTop = 600; });
await panel.screenshot({ path: 'results-panel-view.png' });
A locator screenshot is for isolating one element. A full-page screenshot is for the document’s scrollable extent. If the target lives inside an inner scroller, decide which part of that inner content you want and move the scroller there before capturing. [Locator API] [Page API]
4. Control timing, output, and capture conditions
The locator screenshot accepts screenshot options and locator-operation options. Commonly useful settings include:
path: save the image to a file. Its extension determines the output format when a path is supplied.type: choose'png'or'jpeg'when you need to specify the format explicitly.quality: set JPEG quality; it applies to JPEG, not PNG.timeout: set the maximum time for the locator operation in milliseconds. Check the API documentation for defaults and behavior in your Playwright version.animations: disable or allow animations according to the screenshot you need.omitBackground: make the default white background transparent where supported.scale: choose CSS-pixel or device-pixel output scaling.style: apply a stylesheet during capture when you need to hide or restyle content.mask: visually mask matched locators, useful for dynamic regions in visual snapshots.
Use only options supported by the Playwright version installed in your project; consult the [Locator API reference] for the full option list. Screenshot settings affect the image, but do not change the basic scope: a locator screenshot still captures the matched element.
For content that appears asynchronously, wait for the specific target or its ready state before capturing. Locators automatically wait for their target as part of the operation, but that does not guarantee that every image, chart, or application request inside the target has finished rendering. Prefer waiting for a meaningful selector or state over adding an arbitrary delay.
5. Handle inner scroll areas and tricky targets
Target inside a nested scroller
Playwright scrolls an offscreen locator into view, including when scrolling is needed to reach it. But a screenshot of a scrollable element includes only the content currently visible inside that element. It does not stitch the entire inner scroll area into one image. Scroll the inner container to the required section before capturing, or capture a specific child locator inside it. [Locator screenshot behavior]
Element hidden by an overlay
Scrolling does not dismiss a dialog, sticky banner, or other element covering the target. The screenshot reflects the rendered page, so covered content can remain covered. Close or otherwise handle the overlay if the desired output should show what is behind it.
Element changes during rendering
If an application re-renders the target, keep a locator and use it at capture time rather than relying on a previously resolved element handle. The locator can resolve the current matching element; the screenshot call can still throw if the element detaches during the operation. For pages that update continuously, wait for the relevant UI state to settle before taking the screenshot.
Target is not a single element
A screenshot call needs a locator that identifies the intended target. If a locator matches multiple similar elements, refine it using a section, accessible name, or test ID so the capture is unambiguous.
6. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Locator times out or does not resolve | The selector matches nothing, the content has not loaded, or the locator is ambiguous for the intended target. | Check the selector and page state; wait for the relevant content and use a specific role, name, or test ID. |
| Screenshot contains an overlay instead of the target | A dialog, sticky element, or other content covers the target. | Dismiss or handle the covering UI before capture. Scrolling alone does not uncover it. |
| Screenshot has only part of a panel’s content | The target is in an inner scrollable container. | Scroll that container to the desired position, or capture a specific child. A container screenshot does not capture all of its scrollable interior at once. |
| Screenshot operation throws after the target appeared | The target detached from the DOM during capture, often during a re-render. | Use a locator instead of retaining an old element handle, and wait for the page’s relevant state to stabilize. |
| The image includes more than the target | You used a page screenshot, or the locator identifies a larger ancestor than intended. | Use locator.screenshot() for one element and refine the locator to the desired node. |
| CSS or XPath selector breaks after a redesign | The selector depends on DOM structure that changed. | Prefer an accessible role and name, visible text, a label, or a deliberate test ID. |
7. Performance, reliability, and cost
Playwright’s locator screenshot avoids the extra work of capturing a whole long document when you need only one element. Large targets still produce larger images, and browser launch and page loading can dominate the time for a standalone script. In a test suite, reuse the project’s Playwright browser and page lifecycle rather than launching a fresh browser for every target.
For repeatable visual output, use a stable viewport, wait for the content that matters, and control animations or dynamic regions where appropriate. Locator-based selection is more resilient to re-renders than keeping a stale element reference, though a target that detaches during capture can still cause an error. Playwright is a browser automation library; the capture has no per-screenshot API charge, but running browsers uses your own machine or CI resources.
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A GET request takes a URL and returns an image or PDF. For an element capture, pass a CSS selector with the API’s element option; see the ScreenshotNeo API documentation for the parameter name and supported options.
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);
These supplied calls capture a URL; configure the element selector and any other options using the docs. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
9. Frequently asked questions
Does Playwright automatically scroll an offscreen element into view?
Yes. A locator screenshot waits for actionability checks and scrolls the matched element into view before capture.
Does an element screenshot capture the whole page?
No. It clips the screenshot to the matched element. Use page.screenshot({ fullPage: true }) for the full scrollable document.
Can I capture all content inside a scrollable div?
A screenshot of the scrollable element shows its current interior view. Scroll to the desired content or capture specific child elements; the container screenshot does not stitch all inner content together.
Should I use ElementHandle.screenshot()?
Use locator.screenshot() for current code. Playwright marks the ElementHandle screenshot API as discouraged and points to the locator API. [ElementHandle API]


