Cypress Screenshot Dimensions Differ From Browser Viewport
A Cypress screenshot can differ from the AUT viewport because of capture mode, cropping, element padding, or headless display settings. Here’s how to find the cause and make dimensions predictable.
A Cypress screenshot does not have to match the dimensions you set for the application under test (AUT). The AUT viewport, the area selected for capture, the browser’s headless screen, and the saved image’s pixel dimensions are separate things. First check whether the screenshot is viewport, full-page, runner, clipped, or element-scoped; then inspect the screenshot metadata and the browser launch settings.
For a standard viewport screenshot, set the AUT viewport with cy.viewport(width, height) or Cypress configuration, and use capture: 'viewport'. If you need a particular image-file size, also account for cropping, element padding, and device scale. Cypress does not simulate devicePixelRatio through cy.viewport().
1. Know which dimensions you are comparing
| Measurement | What it controls or describes | Where to check |
|---|---|---|
| AUT viewport | The CSS-pixel viewport available to the application | cy.viewport(), viewportWidth, and viewportHeight |
| Runner preview | The application’s visual size inside the Cypress runner pane; Cypress may scale and center it to fit | The Cypress runner display, not the saved image dimensions |
| Capture area | The content Cypress selects: viewport, full page, runner, or a specified element | cy.screenshot() options |
| Saved image dimensions | The pixel width and height of the output file after capture and any post-processing | Screenshot callback metadata or the image file itself |
| Headless browser screen | The browser display size and scale, which can affect screenshot or video output independently of the AUT viewport | Browser launch options and device scale factor |
Cypress documents a default AUT viewport of 1000 × 660 CSS pixels. The runner may display that viewport at a smaller visual size when it cannot fit in the available pane; that preview scaling does not itself change the configured AUT viewport. Cypress viewport documentation
2. Check the capture mode, crop, and target
The screenshot command supports different capture areas. These modes intentionally produce different images:
capture: 'viewport'captures the AUT in its current viewport.capture: 'fullPage'captures the page from top to bottom, so its height can exceed the viewport height.capture: 'runner'captures the browser viewport, including the Cypress Command Log. Test Replay can hide the Runner UI, which changes what is available in that capture.
A clip rectangle crops the capture. An element screenshot captures the selected element rather than the whole viewport; its padding option can enlarge the resulting image around that element. Review the exact options used at the screenshot call site, including options inherited through helpers. See the Cypress screenshot command and screenshot API references.
3. Set and verify the AUT viewport
Use an explicit viewport when a test depends on a particular layout. The viewport dimensions are CSS pixels, and a viewport call can override the configured default for the test. Cypress resets the viewport to the configured values between tests, so set the desired size in each test or a suite hook when needed.
// cypress/e2e/dashboard.cy.js
describe('dashboard at a fixed viewport', () => {
beforeEach(() => {
cy.viewport(1440, 900);
cy.visit('/dashboard');
});
it('captures the current AUT viewport', () => {
cy.screenshot('dashboard-viewport', {
capture: 'viewport',
overwrite: true,
});
});
});
You can also set the baseline in Cypress configuration. This example uses the current ESM configuration style:
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
viewportWidth: 1440,
viewportHeight: 900,
e2e: {
baseUrl: 'http://localhost:3000',
},
});
Check for all cy.viewport() calls and suite-level test configuration if the effective size differs from the project defaults. Configuration and viewport behavior are documented in the Cypress configuration reference.
4. Log the dimensions Cypress reports
Use Cypress’s screenshot lifecycle callback to record the path, reported dimensions, and whether Cypress scaled the AUT. This helps distinguish an unexpected capture area from a viewport setting issue. The callback metadata is also available to the after:screenshot Node event.
// cypress/support/e2e.js
Cypress.Screenshot.defaults({
onAfterScreenshot($el, props) {
console.info('Screenshot metadata', {
path: props.path,
dimensions: props.dimensions,
scaled: props.scaled,
});
},
});
For a Node-side record, register the event in the configuration file:
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
setupNodeEvents(on) {
on('after:screenshot', (details) => {
console.info('Saved screenshot', {
path: details.path,
dimensions: details.dimensions,
size: details.size,
});
return details;
});
},
},
});
If an after:screenshot handler edits or replaces the image, return details whose dimensions describe the changed file. Otherwise, logs or downstream code may be comparing stale metadata with the processed image. Refer to the Cypress screenshot API for callback and event details.
5. Check headless browser screen size and scale
The browser’s headless screen settings can affect screenshot and video dimensions without changing the AUT viewport configured by viewportWidth and viewportHeight. Cypress documents a Chrome launch example with --window-size=1400,1200. In its non-retina example, device scale factor 1 produces a 1400 × 1200 full-page screenshot, while factor 2 produces 2800 × 2400. Those figures illustrate the relationship; do not assume every browser or CI environment will produce identical output.
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('before:browser:launch', (browser, launchOptions) => {
if (browser.name === 'chrome' && browser.isHeadless) {
launchOptions.args.push('--window-size=1400,1200');
// Set a scale factor only when your test environment requires it.
// Example: launchOptions.args.push('--force-device-scale-factor=1');
}
return launchOptions;
});
},
},
});
Changing the browser screen size does not set the AUT viewport. Configure both when both matter. Cypress also states that cy.viewport() does not simulate devicePixelRatio. See the browser launch API and viewport docs.
6. A reliable debugging checklist
- Write down the expected AUT viewport in CSS pixels and the actual saved image size in pixels. Do not compare the runner preview’s on-screen size with the image file.
- Find the effective viewport settings: configuration values, test-level settings, and every
cy.viewport()call. - Identify the capture mode: viewport, full page, runner, or element. Inspect
clipand elementpadding. - Log callback metadata such as
dimensions,scaled, andpath. If a hook changes the file, ensure it returns updated dimensions. - For headless runs, inspect browser launch arguments, display size, and device scale factor separately from the AUT viewport.
- For visual regression, keep the viewport, browser version, operating system, display scaling, and installed fonts consistent. Cypress notes that differences among these can change rendered pixels and cause comparisons to fail. Cypress visual testing guidance
7. Common causes and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Image is taller than the configured viewport | The capture uses fullPage, or it captures a tall element |
Use capture: 'viewport' for viewport-sized output; use full-page only when the whole document is wanted. |
| Image is smaller or starts at an unexpected point | A clip rectangle crops it, or the screenshot targets an element |
Remove or correct clip; capture the page viewport instead of the element if that is the intended area. |
| Element screenshot has extra border space | Element screenshot padding expands the capture |
Set padding to zero or the intended number of pixels. |
| Runner preview looks small, but callback dimensions are expected | Cypress scaled the preview to fit the runner pane | Compare callback dimensions and the saved file, not the preview’s displayed size. |
| Headless screenshot differs from local run | Browser screen size, device scale, OS, browser version, fonts, or display scaling differ | Pin the viewport and align the rendering environment; inspect launch arguments and scale settings. |
| Configured viewport appears ignored in one test | A test or hook calls cy.viewport(), or a helper changes capture behavior |
Search the test and shared helpers for viewport changes and screenshot options; log the effective values near capture time. |
| Logged dimensions disagree with the output image | An event handler post-processes the screenshot but returns old dimensions | Return updated details describing the output file after processing. |
8. Performance, reliability, and cost considerations
Viewport screenshots generally capture less page content than full-page screenshots, while full-page capture may require Cypress to render and stitch a longer document. Choose the smallest capture area that answers the test’s question. For repeatable runs, explicitly fix the AUT viewport and standardize the browser and operating-system environment. A fixed viewport alone cannot eliminate differences caused by fonts, browser versions, or display scaling.
There is no single output dimension that applies to every capture mode and environment. Treat Cypress’s reported metadata and the actual saved file as the evidence for a particular run. If you resize or post-process images, record the final dimensions separately from the AUT viewport. Cypress documentation describes settings and examples, not a universal runtime or cost benchmark.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request returns a PNG, JPEG, WebP, or PDF. For example, this cURL request saves a WebP screenshot:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options. The same endpoint also works from Python:
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)
And Node.js:
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);
- Cookie banners are accepted and removed before capture; 60+ known consent platforms, newsletter popups, and chat widgets can be removed, with each step configurable.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers report the page verdict and billing status.
- An 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; every feature is available on every plan.
Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card required.
Frequently asked questions
Does cy.viewport(1440, 900) guarantee a 1440 × 900 image file?
It sets the AUT viewport in CSS pixels. The saved file can still differ due to capture mode, clipping, element capture, scaling, or headless display settings.
Does Cypress simulate a retina display with cy.viewport()?
No. Cypress documents that devicePixelRatio is not simulated by cy.viewport(). Browser display scale is a separate setting.
Why does the Cypress runner show a smaller application than my configured viewport?
The runner can scale and center the application preview to fit its pane. That visual presentation does not establish the AUT’s configured viewport size.
Which Cypress setting captures only what a user currently sees?
Use capture: 'viewport' to capture the AUT’s current viewport. A full-page capture includes content beyond the viewport.


