ScreenshotNeo

BlogHow-to

How to Add Visual Regression Testing to Netlify Deploy Previews

Use Playwright to compare reviewed screenshots against Netlify Deploy Previews, with CI configured to wait for the right preview URL before testing.

By the ScreenshotNeo team4 October 20268 min read

Netlify creates a Deploy Preview for a pull or merge request; Playwright Test or a hosted visual-testing service performs the screenshot comparison. The key is to start the visual test only after that request’s preview is ready, then point Playwright at its URL. Netlify does not perform pixel comparisons.

This guide uses Playwright Test with repository-managed screenshot baselines. It covers the test setup, CI handoff, preview access, noise control, troubleshooting, and a ScreenshotNeo option for capturing pages through an API.

1. Confirm Netlify creates an accessible preview

Netlify creates Deploy Previews for pull and merge requests by default unless preview controls have been changed. A preview URL uses a deploy-preview prefix and the request identifier. Netlify also provides a deploy-preview deploy context for context-specific build configuration. See the Netlify Deploy Previews documentation.

  1. Connect the repository to Netlify and confirm Deploy Previews are enabled for the relevant branch and request workflow.
  2. Open a pull request and wait for its Deploy Preview to finish. Record the preview URL reported for that request; do not assume a URL pattern without confirming the actual deployment.
  3. Check that the CI runner can reach the preview. Preview protection may require team login or a password. Arrange authorized access without printing credentials into logs or capturing them in screenshots.
  4. Check which event or status your Git provider and Netlify integration emit when the deploy is ready. Event delivery, timing, URL fields, and permissions vary by repository; validate the real payload before wiring it into CI.

Netlify’s Drawer can collect human feedback, screenshots, and annotations on previews. It is a collaboration feature, not an automated baseline comparison system; see the Netlify preview feedback documentation.

2. Add Playwright visual assertions

Install Playwright Test in the site repository. With npm, the initial setup is:

npm init playwright@latest

Commit the package manifest and lockfile. In CI, use the repository’s locked install command, such as npm ci, then install the browser and operating-system dependencies Playwright needs. The exact install commands for other package managers are in the Playwright CI documentation.

A minimal test can capture the homepage and compare it with a reference image:

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

test('homepage visual baseline', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage.png');
});

Set a base URL so the same test can run locally or against a preview:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://127.0.0.1:3000',
  },
});

For example, a test can navigate to /pricing with page.goto('/pricing'). Choose a small suite of stable, valuable routes and UI states: the homepage, a key conversion page, and a representative interactive state are often more useful than capturing every route. Give snapshots descriptive names so a diff identifies the intended page or state.

On the first run, Playwright generates reference screenshots. Review those images, then commit the accepted baselines with the tests. Later runs compare captured pages to those version-controlled files. Generate and compare references in a consistent browser and CI environment because operating systems and rendering conditions can change pixels. See Playwright’s visual comparisons documentation.

3. Hand the successful preview URL to CI

Run the visual job only after the matching Deploy Preview is available. Configure PLAYWRIGHT_TEST_BASE_URL with the successful deployment’s target URL, then run the suite. Playwright’s CI guide shows a GitHub deployment-status pattern, but that is a pattern to adapt, not a guarantee that every Netlify and GitHub setup emits the same event or URL payload.

The workflow below is pseudocode, not a drop-in Netlify workflow file. Adapt its trigger, permissions, event fields, and URL extraction to the Git provider and integration used by your repository:

on pull request / successful preview deployment:
  install locked project dependencies
  install Playwright browser dependencies
  identify the successful Netlify preview for this request
  verify that the preview URL is present and reachable
  set PLAYWRIGHT_TEST_BASE_URL to that URL
  run the Playwright visual suite
  publish the Playwright report and failure screenshots as CI artifacts

Playwright’s documented GitHub deployment-status example can help shape the handoff. Confirm that the event belongs to the current request and that its target URL is the Deploy Preview URL, not production or an unrelated deploy. A separate CI mechanism can also wait for Netlify to finish and obtain the URL. If a deployment event arrives before the deploy is usable, add a bounded readiness check and fail clearly when the preview never becomes reachable.

A typical command sequence after the URL has been supplied is:

npm ci
npx playwright install --with-deps
npx playwright test

Store test reports and failure artifacts in CI so reviewers can inspect the comparison. Avoid logging secrets used to reach a protected preview, and apply your repository’s retention and access controls to those artifacts.

4. Keep screenshot comparisons stable

  • Fix the environment: use the same browser version, operating system image, fonts, and viewport for baseline generation and CI comparison.
  • Wait for the relevant UI: navigate to the page and wait for a meaningful selector or page state before capturing. Avoid arbitrary long waits when a specific ready condition is available.
  • Control animation: disable or finish animations for the capture where appropriate. Animated elements can differ depending on capture timing.
  • Handle volatile content deliberately: timestamps, rotating promotions, randomized content, and third-party widgets can create diffs unrelated to a code change. Use a test fixture, stable data, or a narrowly scoped screenshot stylesheet to hide or normalize known volatile elements. Playwright supports screenshot options for applying a stylesheet.
  • Choose a capture scope: full-page screenshots are useful for page layout but can be more sensitive to dynamic content and page length. Element screenshots focus review on a stable component when the whole page is not relevant.
  • Review before updating: inspect changed screenshots and update a baseline only when the visual change is intended. Playwright provides --update-snapshots for deliberate updates; do not accept every generated diff blindly.

5. Choose where comparisons and approvals live

For an existing Playwright project, toHaveScreenshot() is a direct way to keep references alongside the tests. Percy’s Playwright client can upload snapshots to a hosted comparison and review workflow. Compare the options against your team’s needs:

Decision Playwright-managed references Hosted comparison service
Baseline location Reference images live with the test suite in the repository. Snapshots are sent to the service for its comparison workflow.
Review process Review diffs through test output and repository or CI artifacts. Review snapshots and diffs through the hosted service workflow.
Preview access The runner must reach the Netlify preview. The capture runner still needs the access required to capture the preview.
Operational dependency Uses the repository’s Playwright and CI setup. Adds a separate service, client integration, and workflow.

Percy documents a Playwright integration. Evaluate service workflow, access, and current terms directly; this guide makes no claims about current prices or plan limits.

Or skip the browser setup

If you need screenshot captures without maintaining browser setup for that capture path, ScreenshotNeo is a website screenshot API and MCP server for developers. It takes a URL in one GET request and returns a PNG, JPEG, WebP, or PDF. It can capture a URL, but Playwright remains the tool in this guide for asserting that a new preview matches a reviewed baseline.

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

See the ScreenshotNeo API documentation for request options and response details. Before capture, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000 shots.

Sign up free for 1,000 screenshots a month, with no card required.

Troubleshooting

Symptom Likely cause Fix
The test hits production, localhost, or the wrong request preview. The base URL was not set, or CI selected the wrong deployment event or target. Fail the job when PLAYWRIGHT_TEST_BASE_URL is missing. Verify the event’s request identifier and target URL before launching Playwright.
Navigation fails or the page is blank. The deploy is still building, the URL is incorrect, or preview protection blocks the runner. Wait for the successful deploy, check the exact URL, and configure authorized runner access without exposing credentials in logs or screenshots.
Every run reports small visual diffs. Browser, operating system, fonts, viewport, animation timing, or volatile page content changed. Pin the CI environment and viewport; stabilize data and animations; mask only known volatile regions.
New baselines differ from local screenshots. Local and CI rendering environments differ. Generate and review baselines in the same environment used by CI, then commit the reviewed images.
A deployment event starts tests too early or lacks a URL. The repository’s integration event timing or payload differs from the assumed pattern. Inspect the actual event payload and deploy lifecycle. Change the handoff to a successful deployment event or another mechanism that waits and obtains the URL.
Snapshot updates hide an unintended regression. Generated references were accepted without reviewing the diffs. Review each changed image and update only the baselines whose new appearance is intended.

Performance, reliability, and cost

Keep the visual suite focused on routes whose appearance matters, and run independent page checks in parallel only as far as the runner and preview can handle reliably. Capturing fewer stable states reduces browser work and the number of diffs reviewers must inspect. The preview deployment itself adds a handoff dependency: if it is unavailable or access is denied, the visual test cannot give a meaningful comparison.

Playwright-managed snapshots add no separate visual comparison service to the workflow described here, but CI still consumes runner time and stores artifacts. A hosted service adds its own workflow and operational dependency; check its current terms and fit directly. No timing or cost benchmark is implied by this guide.

FAQ

Does Netlify compare the screenshots?

No. Netlify provides the Deploy Preview environment. Playwright or a hosted visual-testing service captures and compares rendered pages.

Do baselines belong in the repository?

With Playwright’s built-in screenshot assertions, reference images are part of the test suite. Review and version-control them so changes to expected appearance are visible with code changes.

Can I use ScreenshotNeo instead of Playwright for regression assertions?

ScreenshotNeo can capture a URL through its API, but the workflow here uses Playwright Test to compare against reviewed reference screenshots. Use a comparison workflow that explicitly manages baselines and diffs if you need regression assertions.