Cypress Screenshots Are Blank in Headless Chrome: Fix
Find out whether Cypress failed to save the file, Chrome captured an empty page, or CI showed the wrong artifact—and follow the right fix.
A blank Cypress screenshot in headless Chrome can mean three different things: Cypress did not save or retain the image, Chrome captured an application that had not rendered, or the screenshot file is valid but the CI artifact or preview is blank. Check the PNG and its configured path first; then reproduce the same test in headed Chrome and compare the browser, viewport, and environment. Changing window dimensions is useful for incorrect sizing, but it is not a general fix for empty page content.
1. Identify what is actually blank
Start by separating a missing file from an image that exists but contains blank content, and from a CI preview that fails to show a valid image.
- Look for the image on disk. Cypress uses
cypress/screenshotsby default. Check the configuredscreenshotsFolderif your project overrides it. - Open the PNG locally. If it shows the application, investigate artifact upload, the collected path, and the CI preview rather than browser rendering.
- Check when CI collects files. Cypress clears configured screenshots, videos, and downloads before a run by default. Ensure the workflow collects the new file after the run and from the active screenshots folder.
- If the file exists and its page is blank, continue with the headed-versus-headless comparison below.
Cypress automatically captures failure screenshots during cypress run by default, unless screenshotOnRunFailure is disabled. See the official screenshots and videos guide, cy.screenshot() documentation, and configuration reference for these settings.
2. Reproduce with headed Chrome
Run the same spec with a visible browser and inspect the page state around the failed capture. Cypress documents this command for launching a headed run without exiting immediately:
npx cypress run --headed --no-exit --browser chrome
Compare the headed page and its capture with the headless screenshot, using the same test data and application build. If the headed run also captures a blank page, investigate the app state, navigation, and timing before blaming headless Chrome. If it only happens headlessly, compare browser versions, operating system, viewport, fonts, display scaling, and CI configuration.
Cypress launches browsers headlessly by default for cypress run. Its documented headless defaults are a 1280 × 720 viewport and device pixel ratio (DPR) 1. These defaults determine output dimensions; they do not by themselves explain a completely empty application render. See Launching browsers in Cypress.
3. Check screenshot configuration and capture timing
Inspect the effective Cypress configuration, including any environment-specific overrides:
| Setting | Documented default | What to check |
|---|---|---|
screenshotsFolder |
cypress/screenshots |
CI artifact paths must point to this folder or your configured replacement. |
screenshotOnRunFailure |
true |
If no automatic failure screenshot exists, confirm it has not been disabled. |
trashAssetsBeforeRuns |
true |
Old screenshots are cleared before a run; do not expect a prior image to remain. |
video |
false |
Consider enabling video while diagnosing a confusing failure so you can inspect the run. |
A direct cy.screenshot() call is asynchronous and takes around 100 ms. The application may change before the capture completes. Also, Cypress notes that the Command Log can render asynchronously, so an error shown there may not appear in the screenshot. Wait for the actual application state you need to capture, rather than assuming the command log is part of the image.
// cypress/e2e/dashboard.cy.js
it('captures the rendered dashboard', () => {
cy.visit('/dashboard');
cy.get('[data-cy=dashboard-ready]').should('be.visible');
cy.screenshot('dashboard');
});
Replace the readiness selector with an element that represents completed rendering in your application. Avoid arbitrary delays when a stable, observable readiness condition is available.
4. Investigate new tabs and paused renderers
If the test opens a new tab, commonly through a link with target="_blank", check whether the Cypress tab’s Chromium renderer has been paused. Cypress documents that Chromium will not capture screenshots when the Cypress tab renderer is paused; Cypress attempts to activate that tab during capture. This is a targeted case to investigate when the symptom follows new-tab behavior, not a universal explanation for blank screenshots.
Reproduce the failure without opening a new tab if possible, then compare the page and capture. If the issue occurs only after the tab change, review the navigation and tab-handling flow against Cypress’s screenshot guidance.
5. Compare CI and local browser environments
Normalize the environment before adjusting capture dimensions. Record the Cypress version, Chrome version, operating system, viewport, and whether the run is headed or headless. Check whether the same spec and app build produce the same result locally and in CI.
- Browser version: Chrome is evergreen, and browser updates can affect automated tests. Pin a browser version while reproducing a version-sensitive failure, then test updates deliberately.
- Enterprise or group policy: Browser restrictions can interfere with extensions. Cypress recommends considering Chrome for Testing when enterprise policy causes problems.
- Visual comparison environment: Keep OS, fonts, browser version, viewport, and display scaling consistent when comparing screenshots. Differences in those settings can change output even when rendering works.
- Artifact collection: Confirm the CI upload step runs after Cypress and targets the configured screenshot folder.
Use Cypress’s configuration guide for configuration details and its high-resolution screenshots and videos article for environment and resolution considerations.
6. Adjust dimensions only when size or scaling is wrong
If the image is captured but has the wrong dimensions or scale, configure Chrome’s launch arguments with before:browser:launch. Cypress documents changing headless Chrome dimensions and device scale factor this way. Treat this as a resolution adjustment, not a presumed cure for blank application content.
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('before:browser:launch', (browser, launchOptions) => {
if (browser.family === 'chromium' && browser.isHeadless) {
launchOptions.args.push('--window-size=1440,900');
launchOptions.args.push('--force-device-scale-factor=1');
}
return launchOptions;
});
},
},
});
Choose dimensions that match your test’s intended viewport and keep them stable across runs. Refer to Cypress’s browser launch options for the supported launch hook and examples.
7. Troubleshooting: symptom, cause, and fix
| Symptom | Likely cause | Next step |
|---|---|---|
| No screenshot file appears | Failure capture disabled, wrong folder, or workflow does not export the file. | Check screenshotOnRunFailure, screenshotsFolder, and CI artifact collection after the run. |
| Old image is missing after a run | trashAssetsBeforeRuns clears configured assets before each run. |
Collect the new run’s output; do not rely on a screenshot left by an earlier run. |
| Local PNG is correct but CI preview is blank | Artifact path, upload timing, or preview handling is wrong. | Download and open the artifact; compare it with the file on disk and correct the workflow path or timing. |
| Page content is blank in the PNG | The application may not have rendered before capture, or the headless environment differs. | Reproduce headed, wait for an app-specific ready condition, then compare browser and environment settings. |
| Blank capture follows opening a new tab | Chromium may have paused the Cypress tab renderer. | Reproduce without the tab change and inspect Cypress’s documented paused-renderer behavior. |
| Screenshot has unexpected size or scale | Headless dimensions or DPR differ from the intended capture. | Set and stabilize launch dimensions and device scale factor; verify the result rather than assuming this fixes blank content. |
| Failure changes after a Chrome update | Browser-version change may affect automation. | Pin the browser to reproduce, record the version, then evaluate an update separately. |
8. Performance, reliability, and cost considerations
For a reliable visual test, capture after the application signals readiness, keep the browser and viewport consistent, and retain enough run evidence to distinguish rendering failures from capture and artifact failures. Cypress documents that an explicit screenshot call takes around 100 ms; account for its asynchronous completion when subsequent test steps depend on the saved image. Enabling video adds a diagnostic artifact, while video capture is off by default.
Keep screenshot retention and CI artifact upload intentional: Cypress clears assets before runs by default, and uploading an entire folder for every job may consume storage or time. The exact cost depends on your CI provider and retention policy; check those settings in your workflow rather than assuming Cypress sets the price.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. For an independent page capture, one GET request returns an image or PDF; it does not replace Cypress’s in-test browser state or prove that your application passed its tests. See the ScreenshotNeo API documentation.
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}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
- Cookie and consent banners are accepted before capture, and more than 60 known consent platforms, newsletter popups, and chat widgets are removed; each step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify the page verdict and billing state in headers.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdffor Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
FAQ
Does headless Chrome always produce blank Cypress screenshots?
No. Headless mode is Cypress’s default for CLI runs. A blank result calls for checking file existence, rendered page state, and environment differences separately.
Should I increase the viewport to fix a blank page?
Only if the issue is incorrect size or scaling. A larger viewport is not a general fix for content that did not render.
Can a screenshot API replace Cypress failure screenshots?
No. An API can capture a public page independently, but it does not capture Cypress’s in-test state or replace test-run artifacts.


