Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Jest’s built-in snapshot testing does not compare screenshots. It serializes values such as rendered component output and compares text files. Visual regression testing compares pixels from a browser-rendered page or component. To test appearance, you must render the UI and then compare an image—either by adding an image matcher to Jest or by using a browser test runner such as Playwright.

This guide shows both approaches, explains baseline management and CI stability, and includes a hosted-review option. If you want screenshots without maintaining browser infrastructure, ScreenshotNeo can capture clean images through one API call.

What Jest snapshots do—and do not—test

A normal Jest snapshot test might look like this:

it('renders the button', () => {
  expect(render(<Button>Save</Button>)).toMatchSnapshot();
});

The stored snapshot is serialized text. It can catch changes to component structure, props, and accessible markup, but it cannot prove that the button has the right color, spacing, font, responsive layout, or visual position. Jest’s snapshot documentation distinguishes this serialized-value approach from visual regression, where rendered screenshots are compared pixel by pixel.

A visual test therefore has four stages:

  1. Render a page or component in a controlled browser-like environment.
  2. Capture a PNG (or another image format).
  3. Compare it with a reviewed baseline.
  4. Inspect the diff and update the baseline only when the change is intentional.

Keep both test types when they answer different questions: text snapshots are useful for structure; image comparisons protect appearance.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the right Jest-centered approach

Approach What is compared Where it runs Best fit
Jest toMatchSnapshot() Serialized values or markup Jest test environment Component structure and output contracts
jest-image-snapshot Image pixels Your Jest setup, with an image-producing renderer Teams that want image assertions inside Jest
Playwright toHaveScreenshot() Page or element screenshots Playwright’s browser test runner Real browser states, responsive layouts, and end-to-end flows
Chromatic for Playwright Captured UI states and pixel differences Chromatic’s cloud comparison environment Hosted review and collaboration around browser snapshots

The jest-image-snapshot README states a Jest peer-dependency range of 20 through 29. Verify that range against the version actually installed in your project; compatibility is version-sensitive. Playwright’s assertion belongs to its test runner rather than Jest. Chromatic documents a Playwright integration that uploads captured states for cloud comparison.

Set up visual assertions with Jest and jest-image-snapshot

1. Install a renderer and the matcher

You need a way to turn the UI into an image. A common arrangement is a component renderer that exposes a screenshot or image buffer, plus the matcher package:

npm install --save-dev jest-image-snapshot
# Install the renderer appropriate for your application separately.

The matcher project documents Jest 20–29 as its peer range. Do not assume a newer Jest release is supported without checking the package’s current documentation and lockfile resolution.

2. Register the matcher

Create a Jest setup file, for example test/jest.setup.js:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const { toMatchImageSnapshot } = require('jest-image-snapshot');

expect.extend({ toMatchImageSnapshot });

Reference it in your Jest configuration:

module.exports = {
  setupFilesAfterEnv: ['<rootDir>/test/jest.setup.js']
};

With an ES-module configuration, use the equivalent import syntax supported by your Jest version.

3. Capture the state you care about

The exact capture call depends on your renderer. The important contract is that the renderer returns an image buffer or file that the matcher can compare. A representative test shape is:

const { renderComponentToPng } = require('./renderComponentToPng');

test('checkout button visual state', async () => {
  const image = await renderComponentToPng({
    component: 'CheckoutButton',
    props: { label: 'Pay now', disabled: false }
  });

  expect(image).toMatchImageSnapshot({
    customSnapshotIdentifier: 'checkout-button-enabled'
  });
});

Replace renderComponentToPng with the renderer used by your project. Do not pass HTML or a React tree directly to an image matcher; it needs actual image data.

4. Create and review a baseline

Run the test in an environment with the same fonts, viewport, browser engine, and data used by CI. The first run creates a baseline. Commit that baseline with the test. On later runs, a mismatch should produce an actual image, an expected image, and a diff image (the precise directory names depend on package configuration).

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Review the diff, not just the failure count. A changed font, missing webfont, animation frame, network response, or viewport can create broad differences that are not a product regression. Update the baseline only after confirming that the visual change is intended:

npx jest path/to/visual.test.js -w

Use your project’s documented snapshot-update command or Jest’s update-snapshot option when you deliberately approve a change. Keep baseline updates in the same pull request as the UI change so reviewers can see why pixels moved.

Prefer Playwright for a real browser page

If the subject is a complete page, route, or interaction sequence, Playwright’s browser test runner is usually the more direct model. Its toHaveScreenshot assertion captures a page or locator and compares it with a stored screenshot.

import { test, expect } from '@playwright/test';

test('pricing page desktop view', async ({ page }) => {
  await page.goto('http://localhost:3000/pricing');
  await expect(page).toHaveScreenshot('pricing-desktop.png', {
    fullPage: true
  });
});

test('error banner', async ({ page }) => {
  await page.goto('http://localhost:3000/form');
  await page.getByRole('button', { name: 'Submit' }).click();
  await expect(page.getByRole('alert')).toHaveScreenshot('form-error.png');
});

Install the browsers required by your Playwright version, start the application before the test run, and commit the generated snapshots. Use a locator screenshot for a component-like region and a full-page screenshot for page-level layout. Playwright’s assertion and runner requirements are separate from Jest; do not place toHaveScreenshot in a Jest test unless you have deliberately built an integration around the Playwright runner.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Control sources of nondeterminism

  • Pin the browser version used locally and in CI.
  • Use a fixed viewport and device scale factor.
  • Load the same fonts in every run; wait for fonts before capture.
  • Seed or stub API data so content does not change between runs.
  • Disable animations and transitions, or wait for a stable state.
  • Freeze time when timestamps appear in the UI.
  • Wait for a meaningful selector or network-idle condition instead of sleeping for an arbitrary short delay.
  • Hide volatile regions such as rotating ads, clocks, and randomized avatars.

These controls are engineering practices rather than guarantees of identical pixels across every machine. If rendering still differs, inspect operating-system fonts, browser versions, color profiles, and device scale.

Use hosted review with Chromatic’s Playwright integration

Chromatic documents an integration that captures UI states from Playwright and performs pixel comparisons in its cloud environment. This can be useful when reviewers need a central place to inspect changes instead of downloading diff artifacts from CI. Keep the same discipline: capture representative states, review the visual diff, and approve a new baseline only when the change is intentional. Confirm the integration’s current setup and availability in Chromatic’s documentation before standardizing it; this article does not assert plans, prices, limits, or regional availability.

Design a maintainable visual test suite

Choose states, not every permutation

Cover states that are visually meaningful: default, loading, empty, error, disabled, keyboard focus, authenticated versus signed-out navigation, and key responsive breakpoints. Avoid duplicating dozens of tests that render the same pixels with irrelevant data variations.

Name baselines predictably

Include the component or route, state, and viewport in the test or snapshot name. A name such as cart-mobile-empty makes a failed artifact actionable. Keep one baseline per intentionally distinct viewport rather than relying on a single image to represent responsive behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Review failures as code changes

Require a reviewer to examine the expected, actual, and diff images. A large diff often indicates a test-environment problem; a small localized diff may be the intended CSS change. Never update all snapshots automatically in CI, because that can silently bless a broken layout.

Troubleshooting common failures

“toMatchImageSnapshot is not a function”

The matcher was not registered, or the Jest setup file is not loaded. Check setupFilesAfterEnv, the setup-file path, and that expect.extend runs before tests.

Peer-dependency or install errors

Compare your installed Jest version with the matcher’s documented 20–29 peer range. Resolve the mismatch by using a supported Jest version, selecting a maintained matcher compatible with your version, or moving the test to Playwright.

Every pixel changes on CI

Compare browser and operating-system versions, fonts, viewport, device scale, timezone, locale, and seeded data. Make animations deterministic and wait for fonts and images. Recreate the CI container locally when possible.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The screenshot is blank or incomplete

The capture may occur before navigation, rendering, or lazy assets finish. Wait for a stable selector, ensure the server is reachable from the test process, and verify that the element is visible before capture.

Only dynamic regions fail

Remove or mask timestamps, randomized content, ads, and rotating media. If the region is part of the requirement, replace live data with a fixed fixture rather than weakening the whole comparison.

A legitimate redesign creates hundreds of failures

Separate the intentional change from unrelated noise. Update affected baselines in the same reviewed change, then investigate any unexpected files instead of accepting the entire snapshot set blindly.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a one-off baseline or a CI job, call the API:

See the ScreenshotNeo API documentation for options such as full-page capture, element selectors, device presets, dark mode, custom CSS and JavaScript, waits, request blocking, cookies, headers, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture, and usage reporting.

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}`);

ScreenshotNeo has 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000 shots. Create an account at ScreenshotNeo’s free sign-up page.

Cost and reliability decisions

Self-hosted Jest or Playwright tests consume your CI time and require you to maintain browsers, fonts, fixtures, and diff artifacts. A hosted workflow adds an external service and review process but can centralize comparison. An API is useful when you need screenshots from scripts, bulk URLs, signed public images, or AI-agent access. Whichever route you choose, record the browser, viewport, data source, and baseline version alongside each visual test so a failure is reproducible.

Frequently Asked Questions

Can I use Jest snapshots and visual regression tests in the same project?

Yes. Use toMatchSnapshot() for serialized structure and an image matcher or browser screenshot assertion for rendered appearance; keep their baselines and failure review separate.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should a visual test cover an entire page or one element?

Use an element screenshot for a focused component or state, and a full-page screenshot when navigation, responsive layout, and page composition are the behavior under test.

When should I update a screenshot baseline?

Only after inspecting the diff and confirming that the visual change is intentional, with the baseline update reviewed together with the UI change.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.