ScreenshotNeo

BlogComparisons

Puppeteer vs Cypress for Visual Regression Testing

Compare Puppeteer and Cypress for visual regression testing, with runnable capture examples, baseline options, reliability practices, and a workflow decision guide.

By the ScreenshotNeo team4 October 20269 min read

Short answer: Puppeteer and Cypress can both capture screenshots, but neither screenshot command alone is a complete visual regression system. Puppeteer gives you page and element capture through its browser automation API. Cypress lets you capture screenshots within end-to-end or component tests, but its core does not compare them with approved baselines. To get visual regression testing, add a comparison layer and decide how your team will store, update, and review baselines.

Choose based on your existing test architecture, how much of the capture and baseline workflow you want to own, and which browsers and viewports you need to cover. There is no evidence here for a universal winner on accuracy or speed. For the official capabilities, see the Puppeteer screenshot guide and Cypress visual testing guide.

1. What visual regression testing needs

A visual regression workflow has four steps:

  1. Drive the application into a known state, such as a logged-in page or an open modal.
  2. Capture a page or a focused component.
  3. Compare the new image with an approved baseline.
  4. Review the difference and accept or reject the change.

Screenshot capture is only step two. A captured PNG is useful for debugging, but it does not tell you whether pixels changed compared with a baseline. Cypress explicitly documents this distinction: its core captures screenshots, while image comparison comes from a plugin or service. The Puppeteer screenshot guide documents page and element capture; it does not describe an integrated baseline comparison and review workflow.

2. Puppeteer vs Cypress at a glance

Decision Puppeteer Cypress
Capture Official guide documents page screenshots with Page.screenshot() and element screenshots with ElementHandle.screenshot(). cy.screenshot() captures the app. Cypress also captures screenshots on test failures in cypress run by default.
Comparison Choose and connect a comparison tool or implement a comparison step yourself. Add an open-source image-diff plugin or a hosted visual testing service; Cypress core does not compare against baselines.
Test-state setup Build the browser automation flow that reaches the state you want to capture. Capture inside an existing end-to-end or component test after the test drives the app into the relevant state.
Baseline ownership Depends on the comparison layer you select. Local plugins generally leave baseline storage, updates, and review to your team. Hosted services can manage more of that workflow.
Rendering coverage Depends on your automation and comparison setup. Depends on your local configuration or hosted provider. Some hosted services offer cross-browser or viewport rendering.
Best fit Teams that want to build a browser automation pipeline around their chosen comparison layer. Teams already using Cypress that want screenshots tied to their functional test states.

These are workflow differences, not a claim that one framework produces more accurate or faster diffs. The browser, operating system, fonts, viewport, display scaling, data, and timing all affect the rendered image.

3. Capture a page with Puppeteer

The following runnable example opens a page, waits for a stable application-specific signal, and writes a screenshot. Save it as capture.mjs, install Puppeteer in your project with npm install puppeteer, then run node capture.mjs. Replace the URL and selector with your app’s page and readiness signal.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage({
    viewport: { width: 1365, height: 900 },
    deviceScaleFactor: 1,
  });

  await page.goto('http://localhost:3000/dashboard', {
    waitUntil: 'networkidle2',
    timeout: 30_000,
  });
  await page.locator('[data-testid="dashboard-ready"]').wait();

  await page.screenshot({
    path: 'artifacts/dashboard.png',
    fullPage: true,
    animations: 'disabled',
  });
} finally {
  await browser.close();
}

The artifacts directory must exist before writing the file. For a focused component, get its element and call element.screenshot({ path: 'artifacts/chart.png' }); Puppeteer’s official guide covers page and element screenshots and their options in detail.

A minimal capture is not yet a regression test. Add an image comparison step that reads this output and an approved baseline, reports a diff, and fails CI according to your chosen policy. Keep baseline approval separate from automatic baseline replacement so an unexpected change does not silently become the new expected image.

4. Capture in Cypress

In an existing Cypress end-to-end test, drive the app into a predictable state, assert that it is ready, and capture the page. The command below captures an image; it does not perform baseline comparison on its own.

describe('dashboard visual state', () => {
  it('captures the loaded dashboard', () => {
    cy.clock(new Date('2025-01-15T12:00:00Z'));
    cy.intercept('GET', '/api/dashboard', {
      fixture: 'dashboard.json',
    }).as('dashboard');

    cy.visit('/dashboard');
    cy.wait('@dashboard');
    cy.get('[data-testid="dashboard-ready"]').should('be.visible');
    cy.screenshot('dashboard-loaded', { capture: 'fullPage' });
  });
});

Run this test through your project’s Cypress setup, for example with npx cypress run. Add a compatible image comparison plugin or service and follow its current installation and assertion instructions to compare the image with a baseline. Cypress maintains a directory of plugins and a guide to visual testing; check the current compatibility and maintenance status before choosing one.

Cypress also captures screenshots on test failures in cypress run by default. Those failure screenshots help diagnose a test; they are not a substitute for intentional snapshots and baseline comparison. See the Cypress screenshot and video guide.

5. Choose a comparison and review workflow

Local image-diff plugin

A local plugin can compare captured images in your development or CI environment. Your team owns the expected images, the comparison configuration, and how reviewers inspect and approve diffs. This gives you direct control over the pipeline, but baseline updates and review need a clear process.

Hosted visual testing service

A hosted service can manage more of the baseline, rendering, and review workflow. Depending on the provider, it may offer cross-browser or viewport coverage and a review dashboard. Cypress lists services including Applitools Eyes, Argos, Chromatic, Happo, LambdaTest SmartUI, Percy, Sauce Labs Visual, SmartBear VisualTest, and Wopee.io. Check each provider’s current browser support, integration steps, pricing, and availability before selecting it; those details can change.

How to make the decision

  1. Start with the test runner you already use. If Cypress already establishes the important app states, adding visual capture there avoids building a second state-driving path. If your automation is based on Puppeteer, use its capture API and select a comparison layer.
  2. Choose who owns baselines. Decide where images live, who approves changes, and how branches or intentional redesigns update the expected images.
  3. Set required browser and viewport coverage. Compare the environments your team can run locally with the coverage a hosted provider offers.
  4. Estimate operational effort. Account for CI rendering consistency, image artifacts, review time, and provider costs where applicable.
  5. Run a small pilot. Pick a few stable, high-value pages and components. Measure review noise and workflow fit in your own project rather than assuming a framework-wide speed or accuracy winner.

6. Make screenshots deterministic

Visual diffs become useful when the capture environment and application state are repeatable. Cypress’s visual testing guidance calls out rendering consistency and practical ways to reduce noisy changes. These practices also apply to Puppeteer-based capture.

  • Wait for the page state you need. Assert that expected data and UI are present instead of relying only on a fixed delay.
  • Fix test data and network responses. Use fixtures or network stubbing so content does not change between runs.
  • Control time. Freeze dates and clocks where date-dependent content appears.
  • Use a fixed viewport and rendering environment. Keep viewport size constant, and pin browser versions and operating-system images where practical. Fonts and display scaling can change text wrapping and layout.
  • Stop animation at the source when possible. Disable animations in the test environment or wait for them to finish. Cypress notes that action-command animation settings do not prevent unrelated page animations from appearing mid-snapshot.
  • Mask narrowly. If a dynamic region cannot be stabilized, mask that small region rather than increasing a whole-page comparison threshold.
  • Capture meaningful checkpoints. Prioritize important pages, shared components, and important states. A focused element snapshot can make review easier when full-page captures create noisy diffs.

Use the same baseline-generation environment as the comparison environment for local pixel diffs. Otherwise differences in browser version, fonts, or operating system can look like application changes.

7. Common problems and fixes

Symptom Likely cause Practical fix
Screenshot is blank or missing content The page or app data was not ready when capture ran. Wait for a specific ready selector or response, then assert expected content before capture. Check navigation errors and fixture setup.
Diffs change across CI runs Browser, OS, fonts, viewport, scaling, data, or timing differs. Pin the rendering environment, fix viewport and data, freeze time, and remove or finish animation.
Unrelated page areas create noisy diffs Dynamic content is included in a broad full-page capture. Stabilize its data or mask a narrow region. Consider an element-level capture for the feature under review.
Cypress screenshot exists but the test never reports a visual failure cy.screenshot() captured an image, but no comparison integration is configured. Add a maintained image-diff plugin or hosted visual testing service and configure baseline assertions and review.
Puppeteer image is written but no baseline is checked The capture script has no comparison or CI failure step. Connect a comparison layer, store approved baseline images, and fail the job when the configured difference policy is exceeded.
Animation appears at different frames Capture happened during an animation or a page animation continued despite test command settings. Disable it in the test environment, wait for completion, or use a stable state before capture.
A plugin breaks after a Cypress upgrade Plugin maintenance or version compatibility changed. Check the Cypress plugin directory, the plugin’s supported versions, and its current setup instructions; pin compatible versions while upgrading deliberately.

8. Performance, reliability, and cost

Neither framework’s screenshot API alone determines end-to-end visual test cost. Runtime depends on how many states you visit, the page’s load behavior, screenshot size, comparison work, browser and viewport matrix, and whether rendering or review is hosted. Hosted services may charge according to their own current plans and usage rules; confirm those directly with each provider.

  • Start with a small set of important checkpoints. Capturing every incidental state increases runtime and baseline maintenance.
  • Prefer a stable readiness condition over a long arbitrary wait, while allowing realistic time for required assets and data.
  • Use element captures when they answer a focused question and reduce image size or review noise. Keep page captures where layout context matters.
  • Retain screenshots and diffs as CI artifacts so failures can be investigated. Set an explicit baseline approval process.
  • For reliable comparisons, keep image generation and baseline comparison on the same pinned rendering environment whenever possible.

9. ScreenshotNeo as an alternative to try first

If the immediate need is to capture a clean website screenshot without maintaining browser launch and capture code, ScreenshotNeo is the alternative to try first: cookie banners, popups, and chat widgets are removed before the shot, and only clean shots are billed. ScreenshotNeo is a screenshot API and MCP server; it does not replace a visual regression baseline and review layer.

Or skip the browser setup:

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 docs for the options and request format. 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; and 1,000 screenshots a month are free with no card, with paid plans starting at $5 for 3,000. Sign up for free and get 1,000 screenshots a month with no card.

10. Frequently asked questions

Does Cypress do visual regression testing by itself?

No. Cypress can capture screenshots in its test workflow, but image comparison against approved baselines requires a plugin or service.

Can I use visual snapshots in a Cypress component test?

Cypress supports component testing as well as end-to-end testing. A visual capture can follow the setup and assertions in a component test; comparison still comes from your selected plugin or service.

Should every test take a screenshot?

No. Capture deliberate checkpoints whose visual changes matter and whose diffs your team can review and maintain.

Can a screenshot API replace a visual regression tool?

An API can produce screenshots, but regression testing also needs comparison with a baseline and a process to review changes. Choose and connect those pieces explicitly.

Sources