Quick answer
Call await expect(page).toHaveScreenshot(). First run saves a PNG baseline in __snapshots__/; subsequent runs compare pixel-for-pixel and fail on diffs. Mask timestamps and update baselines with --update-snapshots.
1. Your first visual test
import { test, expect } from '@playwright/test';
test('landing page looks correct', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('landing.png');
});
test('pricing card matches design', async ({ page }) => {
await page.goto('/pricing');
const card = page.getByTestId('plan-pro');
await expect(card).toHaveScreenshot('pricing-pro.png');
});2. Mask everything that changes
await expect(page).toHaveScreenshot({
mask: [
page.getByTestId('timestamp'),
page.getByTestId('user-avatar'),
page.locator('.live-price'),
],
maskColor: '#ff00ff', // default is pink
});3. Tuning thresholds
| Option | Meaning | When to use |
|---|---|---|
maxDiffPixels: 0 | Exact match | Component snapshots |
maxDiffPixels: 100 | Allow up to N different pixels | Small antialiasing noise |
maxDiffPixelRatio: 0.02 | Allow 2% of pixels to differ | Full-page shots with fonts |
threshold: 0.2 | Per-pixel colour tolerance | Rarely — prefer masks |
4. Making snapshots stable across machines
// playwright.config.ts
export default defineConfig({
use: {
// Freeze animations so snapshots aren't captured mid-transition
launchOptions: { args: ['--force-prefers-reduced-motion'] },
},
expect: {
toHaveScreenshot: { animations: 'disabled', caret: 'hide', scale: 'css' },
},
});mcr.microsoft.com/playwright:v{VERSION}-jammy. Font hinting and GPU output are now identical.5. Updating baselines
# Re-record after a legitimate UI change
npx playwright test --update-snapshots
# Update only one project or one file
npx playwright test tests/landing.spec.ts --update-snapshots --project=chromium
# Then commit the __snapshots__ diff and review it in the PR6. Common mistakes
You will chase flakes forever. Snapshot components, or mask everything dynamic.
--update-snapshots without reviewing the diff hides real regressions. Treat baseline PRs as code review.
Fonts render differently on macOS, Windows and Linux. Standardise on Playwright's Docker image.
7. Hands-on task (20 minutes)
- 1
Snapshot a card
Pick one component on your app. Add a
toHaveScreenshottest. Commit the baseline. - 2
Introduce a change
Change a colour or padding. Watch the test fail with a nice side-by-side diff in the HTML report.
- 3
Mask a timestamp
Add a live timestamp near the card. Fix the flaky test by masking that locator.
Ready to ship the whole suite: CI/CD with GitHub Actions.