Playwright Screenshot Is Cropped: How to Fix the Viewport and Page Size
Fix a cropped Playwright screenshot by checking viewport size, full-page capture, clipping, element bounds, and device-pixel scale.
If a Playwright screenshot is cropped, first decide whether you want the visible viewport or the whole scrollable page. page.screenshot() captures the current viewport by default. Use fullPage: true for the entire page, set the intended viewport before navigation, and remove or correct any unintended clip option. Also check that you are not taking an element screenshot and that output pixel scale is not being mistaken for page size.
Playwright’s Page screenshot API documents fullPage, clip, and scale; the emulation guide covers viewport configuration.
1. Identify what the screenshot should contain
| Goal | Use or check |
|---|---|
| What is currently visible | page.screenshot() |
| The whole scrollable page | page.screenshot({ fullPage: true }) |
| A specific rectangle | clip coordinates and dimensions |
| One component or other element | Locator or element screenshot; check the element bounds and scroll position |
A full-page screenshot captures the scrollable page as though it were displayed on a very tall screen. It does not mean that the browser viewport has been resized to the page’s full height.
2. Set the viewport before navigation
Set the context viewport when creating the page, or call page.setViewportSize() before loading the target. This makes the responsive layout use the intended width and height from the start. Playwright cautions that some sites do not expect their viewport to change after navigation.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const context = await browser.newContext({
viewport: { width: 1280, height: 1024 },
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'load' });
await page.screenshot({ path: 'viewport.png' });
await context.close();
} finally {
await browser.close();
}
})();
For an existing page, resize it before navigation where possible:
await page.setViewportSize({ width: 1280, height: 1024 });
await page.goto('https://example.com');
await page.screenshot({ path: 'viewport.png' });
page.setViewportSize() also resets the screen size. If your emulation needs screen and viewport controlled independently, configure the context’s screen and viewport options. When using a device preset, inspect the final configuration order: the preset supplies emulation settings, including a viewport, and a later viewport setting can override it.
3. Capture the full scrollable page
For the entire document rather than only the visible viewport, enable fullPage:
await page.screenshot({ path: 'full-page.png', fullPage: true });
This changes the captured area, not the responsive layout width. If the page should render at a particular desktop or mobile width, set that viewport separately. A very long page can also produce a large image, take longer to capture, and use more memory.
4. Check for an unintended clip rectangle
The clip option limits the output to a rectangle defined by x, y, width, and height. Search the screenshot call and any helper that builds its options. Remove clip if you want the normal viewport or full page; otherwise correct its position and dimensions.
// Fixed rectangle, intentionally cropped
await page.screenshot({
path: 'region.png',
clip: { x: 0, y: 0, width: 800, height: 600 },
});
// Full page: do not pass an unintended clip
await page.screenshot({ path: 'full-page.png', fullPage: true });
5. Confirm whether you are capturing an element
A locator or element screenshot is bounded to that element, not the page. For a scrollable element, an element screenshot shows only its currently scrolled content. If your code uses locator.screenshot() or an element handle’s screenshot method, switch to page.screenshot() when the target is the page viewport or full document. If the element is the intended target, confirm its dimensions and scroll position.
See the official ElementHandle screenshot reference for the element-bound behavior.
6. Distinguish CSS dimensions from image pixel dimensions
The screenshot scale option controls output pixel dimensions. Its API default is device: output pixels correspond to device pixels. Use css for one output pixel per CSS pixel. A high device scale factor can make the image file’s pixel dimensions larger without changing which page area was captured.
// Output one pixel per CSS pixel
await page.screenshot({ path: 'css-scale.png', scale: 'css' });
// Output at device-pixel scale
await page.screenshot({ path: 'device-scale.png', scale: 'device' });
Check the context’s deviceScaleFactor along with scale when comparing the saved image’s pixel dimensions to the CSS viewport. Scale affects resolution; fullPage affects the captured page area.
7. A complete diagnostic example
This runnable Node.js example creates a predictable viewport, waits for page load, takes a viewport capture and a full-page capture, and writes both files:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const context = await browser.newContext({
viewport: { width: 1280, height: 1024 },
deviceScaleFactor: 1,
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'load' });
await page.screenshot({
path: 'viewport.png',
scale: 'css',
});
await page.screenshot({
path: 'full-page.png',
fullPage: true,
scale: 'css',
});
await context.close();
} finally {
await browser.close();
}
})();
Install Playwright in the project and install the browser it uses before running the example. For a site whose content loads after the initial page load, wait for the specific content or state you need before taking the screenshot; a correctly sized capture can still look incomplete if the page has not finished rendering.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Only the first screen appears | fullPage was omitted or is false |
Set fullPage: true for the entire scrollable page. |
| A rectangular portion is missing | An unintended clip is restricting the screenshot |
Remove it or correct its x/y position, width, and height. |
| The page has a mobile or unexpected layout | The context viewport is different from the intended dimensions, or a device preset set it | Set the viewport explicitly before navigation and check option ordering. |
| The file dimensions are larger than expected | scale: 'device' and a device scale factor increase output pixels |
Use scale: 'css' for one pixel per CSS pixel; distinguish resolution from captured area. |
| Only one card or component appears | The code calls a locator or element screenshot | Use page.screenshot() for a page capture. |
| A scrollable panel appears partly empty | An element screenshot includes only that element’s currently scrolled content | Scroll the element to the desired position, or capture the page if that is the actual target. |
| Lower content is blank or missing | Content may be lazy-loaded or rendered after the capture | Wait for the relevant content or state before capture; for full-page capture, verify the target page has populated its scrollable content. |
| Changing the viewport after navigation has no expected effect | The site already initialized its responsive behavior at the earlier size | Set viewport before goto() and navigate again. |
9. Performance, reliability, and cost considerations
- Use the smallest capture area that meets the need. A viewport image is generally smaller than a very tall full-page image. Large captures take more resources to encode and store.
- Make layout inputs explicit. Fix viewport and device scale factor so runs use consistent responsive breakpoints and image resolution.
- Wait for the right condition. Waiting for a relevant selector or page state is more reliable for dynamic content than assuming a fixed delay is enough. Do not capture before images or content needed in the output appear.
- Keep the capture target intentional. Avoid accidentally passing both a restrictive clip and a full-page goal; inspect the final options passed by wrappers and shared helpers.
- Account for browser execution costs. Full-page images consume more bandwidth, storage, and processing than viewport captures. The Playwright documentation cited here does not provide benchmark figures, so actual resource use depends on the page and environment.
10. Or skip the browser setup
ScreenshotNeo is a website screenshot API: send one GET request with a URL to receive an image or PDF. Its API supports full-page capture and configurable viewport dimensions. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An 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. Sign up for the free plan.
FAQ
Does fullPage: true change my viewport width?
No. It captures the page’s scrollable height. Set the viewport separately to control responsive layout width and height.
Should I use scale: 'css' to fix a crop?
Only if the output pixel dimensions are the issue. Scale changes resolution, not the page area included in the screenshot.
Can I set viewport after calling goto()?
You can call page.setViewportSize(), but setting it before navigation is more predictable because some sites initialize their layout from the initial viewport.
Why does an element screenshot omit part of a scrollable panel?
Element screenshots are bounded to the element and show its currently scrolled content. Scroll the element to the desired content or use a page screenshot if you need the page.
Where can I check whether an option changed in my Playwright version?
Use the official Page API reference for the version installed in your project; screenshot behavior and available options can vary by release.


