ScreenshotNeo

BlogHow-to

How to take a Playwright screenshot in a Vite app test

Save a Playwright screenshot from a Vite app, or compare it with a visual baseline. Set up the server, review snapshots, and fix common failures.

By the ScreenshotNeo team4 October 20267 min read

To save an image from a Playwright test, navigate to your Vite app and call page.screenshot({ path: 'screenshots/home.png' }). To detect visual changes automatically, use Playwright Test’s await expect(page).toHaveScreenshot('home.png'); it creates a baseline on the first run and compares later renders against it. These calls serve different purposes: one saves an artifact for inspection, while the other makes visual output part of a test. See the official Playwright Page API and visual comparisons guide.

1. Set up Playwright to start your Vite app

This example assumes the project already has @playwright/test installed, a Vite script named dev, and port 5173 is available. Adjust the command and address for your project. The webServer setting starts the app before the tests; baseURL lets tests navigate with a relative path such as /. Playwright waits for the configured server URL to become available. See Playwright’s web server guide and Vite’s Getting Started guide.

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  use: {
    baseURL: 'http://127.0.0.1:5173',
  },
  webServer: {
    command: 'npm run dev -- --host 127.0.0.1 --port 5173',
    url: 'http://127.0.0.1:5173',
    reuseExistingServer: !process.env.CI,
  },
});

The extra -- passes the host and port flags through npm’s script to Vite. Vite’s standard scripts are commonly named dev, build, and preview, but check your package.json instead of assuming the names or port.

2. Choose whether to save an image or test a visual baseline

Save a screenshot file

Use page.screenshot() when you need an image artifact, such as a capture for local review or debugging. It does not compare the result with an expected image.

// tests/home.spec.ts
import { test } from '@playwright/test';

 test('save the homepage screenshot', async ({ page }) => {
  await page.goto('/');
  await page.screenshot({ path: 'screenshots/home.png', fullPage: true });
});

Create the screenshots directory before running this test if it does not already exist. The path option writes the file; fullPage: true captures the full scrollable page instead of only the current viewport. For a viewport capture, omit fullPage.

Compare the page with a visual baseline

Use toHaveScreenshot() when a changed render should fail the test for review. Import both test and expect from @playwright/test and run the test with the Playwright Test runner.

// tests/home.spec.ts
import { test, expect } from '@playwright/test';

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

Playwright’s page screenshot assertion “will wait until two consecutive page screenshots yield the same result, and then compare the last screenshot with the expectation.” That stability check helps avoid comparing while the page is still changing. See the PageAssertions API.

3. Create and maintain visual baselines

  1. Run the test once. Playwright reports that the expected snapshot is missing and produces an image to use as the initial baseline.
  2. Open and review the image. Confirm it shows the intended state, then add the generated snapshot directory to version control.
  3. Run the test again. Playwright captures the page and compares it with the checked-in expectation.
  4. If an intentional UI change causes a mismatch, run npx playwright test --update-snapshots, inspect the new image and diffs, and commit only approved baseline changes.

Do not update snapshots automatically just to make a failure disappear. A baseline defines what the test considers the expected appearance, so changing it without review can accept an unintended regression. The visual comparisons guide explains snapshot creation and updates.

4. Choose the Vite server that matches the test

Target Use it when Typical setup
Vite development server You want to test the app served during development. Start npm run dev; commonly port 5173.
Vite preview server You want to test the output of a production build. Build first, then serve dist with npm run preview; Vite’s default preview port is 4173.

For a production-output check, change the web server command, readiness URL, and base URL together. This example assumes the usual scripts and default preview port; match it to the project’s scripts and Vite configuration. The Vite preview server serves the built output locally; see Vite’s static deployment guide and Playwright’s web server documentation.

// playwright.config.ts — test the built app
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: 'http://127.0.0.1:4173',
  },
  webServer: {
    command: 'npm run build && npm run preview -- --host 127.0.0.1 --port 4173',
    url: 'http://127.0.0.1:4173',
    reuseExistingServer: !process.env.CI,
  },
});

Use the development server when the claim is about the dev-served app, and preview when the claim is about the built assets. Keeping the target explicit makes the test result easier to interpret.

5. Make screenshot assertions dependable

Keep the rendering environment consistent

Screenshot output can vary with host operating system, browser version, browser settings, hardware, power source, and headless mode. Create and compare baselines in the same environment where possible. If your Playwright configuration runs multiple browser projects, review the baseline for each browser rather than assuming one browser’s image represents all of them. See Playwright’s visual comparisons guide and browser documentation.

Control real sources of nondeterminism

Animations are disabled by default for screenshot assertions. For other changing regions, Playwright supports screenshot assertion options such as stylePath, which applies a stylesheet that can hide dynamic elements. Use masking or hiding only for genuine nondeterminism; hiding content that should be tested can conceal a regression. The PageAssertions API documents the available assertion options.

Also make the page state intentional before capture: navigate to the relevant route, wait for application-specific content when necessary, and use ordinary web-first assertions for important behavior. A screenshot is useful for appearance, but it does not replace checks for text, visibility, navigation, or other behavior.

6. Troubleshoot common failures

Symptom Likely cause Fix
The app is unreachable before the test starts. The server command, host, port, or readiness URL does not match. Check the Vite script and configured port. Keep webServer.url and baseURL aligned, and pass Vite flags after --.
The test cannot find toHaveScreenshot. The assertion import or runner is wrong, or the project uses a different Playwright package. Import expect from @playwright/test and run the test with Playwright Test.
The screenshot file is not written. The output directory in the path does not exist or the process cannot write there. Create the directory before the test and choose a writable path. Use toHaveScreenshot() instead if the intent is a managed visual baseline.
The first visual assertion fails because a snapshot is missing. No expected baseline exists yet. Review the generated image, then add the approved snapshot to version control.
A snapshot fails after a UI change. The render differs from the expected image; the change may be intentional or a regression. Inspect the diff and page state. Update the baseline only after confirming the visual change is intended.
Snapshots differ on a developer machine and CI. Browser or host rendering environments differ. Use a consistent browser and operating system for baseline generation and comparison, or maintain and review environment-specific baselines.
The test captures an unexpected blank or partial view. The page may not have reached the intended state, or the test may be aimed at the wrong server target. Check the route and app state, add a meaningful locator assertion before capture, and verify whether the test should use Vite dev or preview.
The test exercises source code when production output was intended. The configuration starts Vite dev rather than serving the build. Run the build and configure Playwright to start Vite preview at its actual address.

7. Performance, reliability, and cost

Local Playwright screenshots run as part of your browser test workflow. Full-page captures include more page content than viewport captures, and visual assertions need stable consecutive screenshots before comparing. Keep the captured area focused on the behavior you want to review, and avoid adding repeated screenshots where a normal assertion already covers the requirement.

Reliability depends on a ready server, an intentional page state, and consistent rendering conditions. Snapshot diffs are signals for review, not proof by themselves that a change is a bug: examine whether the change was expected and whether environment differences explain it. The Playwright and Vite setup shown here uses local project tools; no hosted screenshot service is required for this workflow.

Or skip the browser setup

If you need a screenshot of a public website rather than a visual regression test of your local Vite app, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for request options.

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}`);
  • Cookie banners are accepted and removed before capture; newsletter popups and chat widgets are removed too. Each step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers report the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients.
  • The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots.

Start free with 1,000 screenshots a month and no card.

FAQ

Does page.screenshot() create a visual regression test?

No. It saves an image. Use expect(page).toHaveScreenshot() to compare a capture with a baseline.

Can I use relative paths in page.goto()?

Yes, when Playwright’s baseURL is configured; for example, page.goto('/').

Should I commit screenshot baselines?

Review and commit the approved expected images so later test runs can compare against them.

Should I use Vite dev or preview?

Use dev to test the development server and preview to test output produced by a build.