How to Run Visual Regression Tests for a SvelteKit Website
Use Playwright to compare SvelteKit pages against reviewed screenshot baselines. Set up stable captures, handle dynamic content, and run the checks in CI.
For a SvelteKit website, a practical starting point for visual regression tests is Playwright Test running against the app. Navigate to representative routes and states, assert screenshots with expect(page).toHaveScreenshot(), commit the approved baselines, and inspect image diffs before accepting changes. Keep the browser and operating-system environment consistent between baseline creation and later runs.
This guide tests the running SvelteKit application. It does not depend on a special SvelteKit Playwright configuration: the key pieces are a reachable app server, Playwright Test, and stable page content. See the Playwright visual comparisons documentation for assertion and snapshot behavior.
1. Choose routes and states worth checking
Begin with a small suite that covers high-value pages and meaningful states. For example:
- The public home or landing page.
- A key user journey, such as a form or account page.
- A representative populated state and an empty or error state where those states matter.
Choose states that catch meaningful design changes without creating a large set of redundant images. A route screenshot checks the composition of the page; a component-focused gallery can cover reusable component variants more directly.
2. Install Playwright Test and configure the app server
Install Playwright Test in the project and install its browser binaries. The commands below are the usual npm setup; use the package manager your project already uses.
npm install --save-dev @playwright/test
npx playwright install
Add a Playwright configuration that starts the SvelteKit app using the project’s existing script. This example assumes npm run dev -- --host 127.0.0.1 serves the app at port 5173. Adjust the command and URL to match your own project. For CI, a production preview server can be a better target if you specifically want to test the built output.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests/visual',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
reporter: 'list',
use: {
baseURL: 'http://127.0.0.1:5173',
browserName: 'chromium',
viewport: { width: 1440, height: 900 },
colorScheme: 'light',
locale: 'en-US',
timezoneId: 'UTC',
screenshot: 'only-on-failure'
},
webServer: {
command: 'npm run dev -- --host 127.0.0.1',
url: 'http://127.0.0.1:5173',
reuseExistingServer: !process.env.CI,
timeout: 120_000
}
});
This is a general Playwright setup example, not a documented SvelteKit-specific recipe. If your app needs environment variables, a database, or other services to boot, provide deterministic test configuration through your normal development or CI setup. Keep secrets out of committed tests and snapshots.
3. Write a screenshot assertion
Create a visual test such as tests/visual/home.spec.ts. Playwright takes repeated screenshots on the first assertion until it gets two consecutive matching captures, then saves the reference. Later runs compare against that reference.
// tests/visual/home.spec.ts
import { test, expect } from '@playwright/test';
test('home page visual baseline', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('home.png', {
fullPage: true
});
});
test('pricing page visual baseline', async ({ page }) => {
await page.goto('/pricing');
await expect(page).toHaveScreenshot('pricing.png', {
fullPage: true
});
});
Use stable, representative test data. If a page depends on API responses you do not control, mock them with Playwright routing so the page renders the same content on every run. Make sure the test waits for the actual state you want to capture rather than relying on arbitrary timing.
4. Create, inspect, and commit baselines
- Run
npx playwright testonce to generate initial expected screenshots. - Open each generated image and confirm it shows the intended route and state.
- Commit the baseline files alongside the tests. Playwright stores snapshots near the relevant test file by default.
- On future runs, inspect the actual, expected, and diff images when an assertion fails.
A baseline is an approved reference, not an automatic record of whatever the page currently renders. When a failure occurs, first decide whether the changed appearance is intended. Fix unintended UI changes in the application. For an intentional redesign, regenerate deliberately:
npx playwright test --update-snapshots
Review the changed snapshot files before committing them. Avoid updating baselines automatically in response to a failed CI run; that can approve an unintended regression.
5. Keep screenshot comparisons stable
Pixels can vary with operating system, browser version, rendering settings, hardware, power state, and headless mode. Run baseline generation and comparison in the same environment for meaningful diffs. A container or fixed CI image can help keep that environment consistent. See Playwright’s visual comparison guidance and CI guide.
Control changing inputs
- Network data: use fixed fixtures or mock relevant requests with
page.route(). - Time-dependent UI: freeze or inject a known date through the app’s test setup; avoid rendering live timestamps in the capture when they are irrelevant.
- Animations: disable nonessential transitions in test mode or use screenshot styles to suppress them.
- Ads, embeds, and remote content: mock or hide content that is outside your control, while keeping product UI visible.
- Fonts and images: serve predictable assets locally where possible, and wait for critical fonts or images before capturing.
Playwright’s screenshot assertion accepts a stylePath option to apply CSS while capturing, which is useful for filtering volatile regions. Keep this stylesheet narrowly scoped: hiding a region that can regress makes the test less useful.
await expect(page).toHaveScreenshot('dashboard.png', {
fullPage: true,
stylePath: './tests/visual/screenshot.css'
});
/* tests/visual/screenshot.css */
/* Example only: hide a nonessential live clock during screenshot capture. */
.test-live-clock {
visibility: hidden !important;
}
Set comparison tolerance carefully
Playwright supports options such as maxDiffPixels and threshold to control acceptable pixel differences. Start with strict comparison and use a tolerance only when you understand the source of harmless variation. A permissive threshold can hide small but real visual regressions. Consult the current option documentation for supported values and defaults.
6. Run visual tests in CI
CI should install dependencies and browser dependencies, start the app through the configured web server, and run the test suite in the same browser and operating-system environment used to create baselines.
npm ci
npx playwright install --with-deps chromium
npx playwright test
If CI is resource constrained, use a conservative worker count until runs are repeatable; increase parallelism after confirming it does not introduce rendering noise or resource contention. Keep snapshot updates as a reviewed code change rather than silently rewriting references during CI. Playwright’s CI documentation describes browser installation and CI execution.
7. Test reusable components separately when useful
Page-level screenshots cover layout, route composition, and user journeys. For a reusable component with many variants, a small gallery page can make focused visual checks faster and easier to understand. Playwright’s component-testing approach uses a project-served gallery and recognizes Svelte where the development server can render it; this is a framework-agnostic approach rather than a turnkey SvelteKit-specific integration. See Playwright component testing.
Storybook offers a story-based workflow: its visual testing guide documents screenshots of stories and a Chromatic-backed review flow. For existing Playwright journeys, Chromatic for Playwright documents uploading page archives for hosted snapshot review; its setup documentation lists Chrome and Playwright 1.38.0 or later for the described integration. Verify current version and plan requirements before adopting a hosted workflow.
Compare approaches by test scope, where references live, how reviewers approve changes, browser and viewport coverage, CI requirements, handling of dynamic content, debugging detail, and total service cost. Applitools also describes visual testing for web applications and Playwright integration in its web testing overview; confirm current integration and commercial terms for your setup.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| First run fails because no expected image exists | The baseline has not been generated yet. | Run the test locally, inspect the created image, then commit the approved snapshot. |
| Images differ on every run | Dynamic content, animation, remote data, or inconsistent rendering environment. | Stabilize inputs, mock network responses, suppress only irrelevant volatile content, and pin the browser and OS environment. |
| CI reports differences that do not appear locally | Different browser build, operating system, fonts, viewport, or rendering mode. | Generate and compare baselines in the same CI image and keep browser versions aligned. |
| Screenshot is blank or partly rendered | The app did not reach the expected state, data did not load, or the server URL is wrong. | Confirm the configured server URL, wait for a meaningful locator or app state, and inspect the test’s failure screenshot. |
| Large pages are clipped or unexpectedly tall | Full-page capture interacts with fixed-position elements or content loaded only while scrolling. | Check the actual full-page image, trigger required lazy loading, and test whether a viewport screenshot or targeted element capture better matches the requirement. |
| Baseline updates include unrelated changes | Many routes or states changed, or an environment changed globally. | Review diffs by route, restore unintended updates, and separate environment changes from intentional UI changes. |
| Tests time out before capture | The dev server is slow to start or the test waits for a condition that never occurs. | Verify the server command and readiness URL, increase startup timeout when justified, and wait for a specific visible state instead of an unrelated network-idle condition. |
9. Performance, reliability, and cost
Visual suites consume browser time and produce image artifacts that reviewers must inspect. Keep the initial route set small, reuse the same browser configuration, and add coverage where a visual failure would matter. Full-page shots are useful for long layouts but take more pixels to compare and can amplify volatile content; capture a specific component or viewport when that better matches the risk.
Reliability mostly comes from controlled inputs and a stable rendering environment. A failing diff is a signal to inspect, not proof by itself that the UI is wrong: confirm whether the source is an intended change, an environment change, or unstable page content. CI retries can help diagnose intermittent infrastructure failures, but they do not fix nondeterministic rendering.
Local Playwright screenshots use the browser and CI resources your team already operates; the main costs are setup, compute, artifact storage, and review time. Hosted visual testing adds a service and its own plan or usage terms. Those terms vary and should be checked directly with the provider before choosing a paid workflow.
Or skip the browser setup
If you need screenshots for documentation, previews, or a review workflow rather than browser assertions inside Playwright, ScreenshotNeo is a website screenshot API and MCP server. Its one-request API returns a PNG, JPEG, WebP, or PDF; the API documentation covers request options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://your-sveltekit-site.example \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://your-sveltekit-site.example"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://your-sveltekit-site.example'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Screenshot capture can support visual review, but it does not replace Playwright’s automated assertions against committed baselines.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
FAQ
Does this require a SvelteKit-specific visual testing plugin?
No. The example runs Playwright Test against the SvelteKit app as a website. The cited Playwright docs provide the screenshot assertion and general browser workflow.
Should I commit screenshot baselines?
Yes. Keep approved references under version control so a code change can be reviewed alongside its visual effect.
Can a screenshot test prove that the page works?
No. It checks rendered appearance at the captured state. Keep functional assertions for behavior, navigation, and data handling.
When should I use component screenshots instead of route screenshots?
Use component-focused captures when many variants of a reusable UI element need coverage; use route screenshots for composition and user journeys.


