How to Capture Baseline Screenshots for Visual Regression Tests in Cypress
Capture repeatable Cypress screenshots, compare them with approved baselines, and reduce false positives with deterministic data, timing, and rendering.
To capture baseline screenshots for visual regression tests in Cypress, drive the application to a known state, wait for it to render consistently, then use a visual testing plugin or service to capture the image and compare it with an approved baseline. Cypress’s cy.screenshot() saves an image; it does not compare screenshots or manage approved baselines. Keep the viewport, browser, operating system, data, time, and page state consistent so a diff points to a UI change rather than incidental rendering noise.
This guide shows how to prepare a Cypress test, choose a comparison workflow and capture scope, manage baselines, and troubleshoot unstable diffs. It also distinguishes Cypress’s built-in screenshot command from the comparison step, which comes from the visual testing tool you choose.
1. Understand what a baseline screenshot is
A baseline is an approved image of a specific, meaningful application state. A visual regression check captures that state again and compares the new image with the baseline. A difference prompts review; it does not automatically mean the change is a defect. If the visual change is intentional, approve or regenerate the baseline through your tool’s review workflow.
Cypress can capture screenshots, but image comparison, baseline storage, and approval are supplied by a plugin or hosted visual testing service. Their commands and configuration differ. Do not assume a third-party command such as cy.compareSnapshot() is built into Cypress.
2. Choose a visual comparison workflow
| Approach | What the team manages | Questions to answer |
|---|---|---|
| Local open-source plugin | Baseline files, comparison in the team’s environment, and CI artifact review. | Where are baselines stored? How are diffs reviewed? Who updates them? Does the plugin fit your Cypress version and CI setup? |
| Hosted visual testing service | The service may provide rendering, baseline storage, comparison, and review workflows; the exact features vary. | Which browsers and viewports are covered? How does review work in CI or pull requests? What are the current terms and costs? |
Cypress’s visual testing guide names tools including Applitools Eyes, Argos, Chromatic, Happo, LambdaTest SmartUI, Percy, Sauce Labs Visual, SmartBear VisualTest, Wopee.io, and open-source options such as Cypress Image Diff, Cypress Image Snapshot, Cypress Visual Regression, Visual Regression Diff, and Pixeleye. They do not all capture or compare images the same way. Check each provider’s current official documentation for setup, Cypress compatibility, capture model, masking, comparison controls, and baseline approval steps before adopting it.
3. Make the page state reproducible
Most noisy screenshot diffs come from uncontrolled inputs or rendering conditions. Stabilize these before selecting a comparison threshold.
- Use a fixed viewport. Set the same width and height for baseline generation and later comparisons. A responsive breakpoint can change layout with a small width difference.
- Keep the rendering environment consistent. Use the same CI image, browser, operating system, fonts, and display scaling for baseline and comparison runs. Pin browser versions where practical.
- Control network data. Stub changing API responses with
cy.intercept()and fixtures when the test is about presentation rather than live data. - Control time. Use
cy.clock()when dates, countdowns, or time-dependent UI would otherwise differ between runs. - Wait for the intended state. Assert that important content is visible and loading indicators or transitions have completed. Avoid arbitrary sleeps when an observable condition can be asserted.
- Deal with animation deliberately. Disable or wait for animations if they produce unstable frames. Cypress’s
waitForAnimationsandanimationDistanceThresholdsettings apply to action commands; they do not guarantee that an unrelated animation will not be in progress when a screenshot is captured. - Keep the page deterministic. Control randomized content, rotating promotions, carousels, personalized content, and third-party widgets when they affect the captured area.
4. Write the Cypress test and capture a baseline
The test below uses Cypress actions and assertions to establish the state, then calls cy.screenshot() to save an image. It is runnable as a Cypress spec for an app that has the example todo UI. It creates a screenshot artifact; connect the marked point to the documented command for your selected visual testing plugin or service to make it a baseline comparison.
describe('todo visual state', () => {
beforeEach(() => {
cy.viewport(1280, 800)
cy.visit('/')
})
it('captures a completed todo state', () => {
cy.get('.new-todo').should('be.visible').type('write tests{enter}')
cy.contains('.todo-list li', 'write tests')
.find('.toggle')
.check()
cy.contains('.todo-list li.completed', 'write tests').should('be.visible')
// Cypress saves an image. A visual testing tool must compare it
// with an approved baseline and provide the review workflow.
cy.screenshot('completed-todo', {
capture: 'viewport',
blackout: [],
overwrite: true
})
// Replace or supplement cy.screenshot() with the selected tool's
// documented snapshot-and-compare command.
})
})
For a real application, use the app’s actual route, selectors, and state. Keep functional assertions even when a visual comparison is present: they make failures easier to diagnose and ensure the test reached the intended state before capturing.
Configure stable test data
describe('account summary visual state', () => {
beforeEach(() => {
cy.clock(new Date('2025-01-15T12:00:00Z'))
cy.intercept('GET', '/api/account/summary', {
fixture: 'account-summary.json'
}).as('summary')
cy.viewport(1280, 800)
cy.visit('/account')
cy.wait('@summary')
cy.get('[data-testid="account-summary"]').should('be.visible')
})
it('captures the loaded summary', () => {
cy.screenshot('account-summary', { capture: 'viewport' })
// Invoke your visual tool's documented comparison command here.
})
})
Place the fixture at cypress/fixtures/account-summary.json and match its structure to the application’s API response. If the test uses a hosted service that captures DOM snapshots or renders elsewhere, follow that service’s data and capture guidance rather than assuming it will use Cypress’s local screenshot artifact.
5. Select the right capture scope
Choose the smallest scope that represents the design or behavior you want to protect. Narrow snapshots can make failures easier to own; full-page snapshots are useful when page-level layout is the contract.
| Scope | Use it for | Tradeoff |
|---|---|---|
| Viewport | A visible screen or responsive breakpoint. | Content outside the viewport is not part of the image. |
| Full page | Page-wide layout and content flow. | Unrelated regions can trigger a diff; long pages may include dynamic or lazy content. |
| Element | A component or region with a clear owner. | The selector must identify the intended element, and surrounding context is excluded. |
| Cypress runner | Debugging the test runner and application together. | Usually not the right unit for a user-facing visual baseline. |
Cypress documents viewport, fullPage, and runner as capture choices; the documented default is fullPage. An element screenshot is captured from the yielded DOM element and ignores the capture option. Set the scope explicitly so a configuration change does not silently alter what the test protects.
// Viewport screenshot
cy.screenshot('settings-viewport', { capture: 'viewport' })
// Full-page screenshot
cy.screenshot('settings-full-page', { capture: 'fullPage' })
// Element screenshot: the yielded element determines the capture scope
cy.get('[data-testid="settings-panel"]')
.screenshot('settings-panel')
6. Cypress screenshot options and artifact behavior
cy.screenshot() can capture the application and, depending on configuration, the Cypress Command Log. It can be called from cy or from a command yielding a single DOM element. The options below describe Cypress screenshot behavior; a visual plugin or service can have separate options.
| Option or behavior | What to know |
|---|---|
capture |
Choose viewport, fullPage, or runner. Cypress documents fullPage as the default; element screenshots ignore this option. |
blackout |
Provide selectors for regions to hide in the screenshot, such as sensitive content. Confirm selector behavior for your Cypress version. |
| Screenshot defaults | Cypress provides configuration for capture behavior and for disabling timers and animations during capture. Check the screenshot API and defaults documentation for your installed version. |
| Output directory | Screenshots are saved under cypress/screenshots by default. Configuration can change the directory. |
| Name and duplicates | A supplied name identifies a screenshot; duplicates get a numeric suffix unless overwrite: true is used. |
| Spec and test organization | Cypress organizes screenshots relative to the spec path and test name. |
| Failure screenshots | During cypress run, Cypress automatically captures screenshots on test failure by default. It does not do this automatically in cypress open. These are debugging artifacts, not approved visual baselines. |
| Capture timing | Capture is asynchronous. Cypress says it takes around 100 ms, and the page may change between issuing the command and the completed capture. Stabilize the page before calling it. |
Screenshot options and defaults can vary by Cypress version. Consult the Cypress screenshot API and its linked defaults documentation for the version in your project.
7. Review and update baselines responsibly
- Run the test in the same environment used for comparison.
- Inspect the diff and the captured image, not just the pass or fail status.
- Decide whether the change is an intended UI update or a regression.
- For an intended change, use your selected tool’s documented approval or baseline regeneration flow.
- For an unintended change, fix the UI or the test’s uncontrolled inputs, then rerun the comparison.
Keep snapshots focused and purposeful. A large collection of redundant baselines creates review work and can lead to approving diffs without understanding them. Mask unavoidable dynamic regions narrowly when the tool supports masking; do not use a broad threshold to hide variability that you could control. Visual comparisons complement functional assertions and accessibility checks; an image diff cannot establish whether text contrast meets an accessibility standard.
8. Troubleshooting common Cypress visual test failures
| Symptom | Likely cause | What to do |
|---|---|---|
| The screenshot changes on every run. | Variable data, time, animation, randomized content, or third-party widgets. | Stub responses, freeze time, wait for a stable state, and disable or control moving content. Mask only unavoidable regions. |
| The diff appears across most of the page. | Viewport, browser, operating system, fonts, display scaling, or browser version differs. | Compare in the same pinned CI image and browser with a fixed viewport and consistent fonts. |
| The screenshot shows a loading state. | The capture ran before the request or client-side render completed. | Wait for the relevant intercepted request and assert that expected content is visible before capture. |
| A full-page image contains missing lazy-loaded content. | Content loads only when scrolled into view or after an interaction. | Drive the page through the interactions or scrolling needed to load the content, then assert that it is present. Confirm how your chosen tool handles full-page capture. |
| A visual command is reported as unknown. | The example command belongs to a plugin, or the plugin was not installed or registered. | Install and configure the chosen integration using its current documentation. Cypress’s built-in screenshot command is cy.screenshot(); comparison commands are tool-specific. |
| Screenshots are missing from the expected directory. | The project uses a different configured screenshots folder, the spec path changes the output structure, or the command failed. | Check Cypress configuration and the runner output. The default folder is cypress/screenshots. |
| Duplicate files appear with numbered suffixes. | The same name was captured more than once and overwrite was not enabled. | Use distinct names for separate states, or set overwrite: true when replacing the same artifact is intended. |
| A capture includes private or irrelevant content. | The screenshot covers a sensitive or dynamic region. | Use Cypress’s blackout selectors or the visual tool’s documented masking option. Keep the mask narrow and verify it hides the intended element. |
| The screenshot catches an animation mid-frame. | An unrelated animation continued while capture began. | Disable the animation for the test or wait for a stable state. Action-command animation settings do not stop every page animation during capture. |
| A test passes functionally but fails visual review. | The UI changed, the baseline is stale, or the image comparison is sensitive to rendering variation. | Inspect the diff, verify the environment and inputs, then fix the regression or approve the intentional change using the tool’s workflow. |
9. Performance, reliability, and cost
Each screenshot adds capture and comparison work, storage or artifact handling, and human review when the image changes. Cypress reports that screenshot capture takes around 100 ms, but end-to-end cost also depends on page loading, the selected comparison tool, image size, and CI. Treat the capture time as one part of the test, not a benchmark for the full visual workflow.
For reliable CI, run baseline generation and comparison with consistent browser and operating system environments, control requests and time, and retain enough artifacts to inspect failures. Hosted services can manage parts of rendering, baseline storage, comparison, and review in exchange for a subscription; local plugins keep the workflow in your environment but leave maintenance, storage, and review to your team. Compare the current feature set, terms, and costs for the options you evaluate. Do not choose a comparison threshold before eliminating avoidable nondeterminism.
10. Or skip the browser setup
If you need website screenshots outside a Cypress baseline workflow, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It returns a PNG, JPEG, WebP, or PDF from one GET request. See the ScreenshotNeo API documentation for request options. This API call captures a page; it does not replace a Cypress visual testing plugin’s baseline approval and comparison workflow.
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);
Replace YOUR_API_KEY with your key. The Node.js example uses Bun’s file-writing helper; in Node.js, save the response body with writeFile from node:fs/promises:
import { writeFile } from 'node:fs/promises';
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 writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers say the page verdict and whether it was billed.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
11. Frequently asked questions
Does Cypress compare screenshots by itself?
No. Cypress captures images with cy.screenshot(); use a visual testing plugin or service for baseline comparison and review.
Should I use a full-page or element screenshot?
Use full-page capture when page-wide layout is the contract under test. Use an element capture when a component has a clear boundary and should fail independently.
Why does the screenshot differ from my local baseline in CI?
Check browser and operating system versions, viewport, fonts, display scaling, time-dependent content, and network responses. These often change pixels even when the application code is unchanged.
Can I use failure screenshots as visual baselines?
Failure screenshots help diagnose Cypress test failures. They do not provide the approved-baseline comparison and review workflow of a visual testing integration.
Does increasing a diff threshold fix flaky snapshots?
It can hide small differences, but it does not control the variable input that caused them. First stabilize data, timing, animation, and rendering conditions; use comparison controls only for residual noise you understand.


