Overview
Playwright provides powerful visual comparison capabilities to detect visual regressions. Compare screenshots pixel-by-pixel to ensure your UI remains consistent across changes.
Screenshot Assertions
The toHaveScreenshot() assertion compares screenshots and highlights differences.
Basic Screenshot Test
Element Screenshot
Named Screenshots
import { test , expect } from '@playwright/test' ;
test ( 'visual regression test' , async ({ page }) => {
await page . goto ( 'https://example.com' );
// First run will generate the baseline screenshot
// Subsequent runs will compare against the baseline
await expect ( page ). toHaveScreenshot ();
});
Screenshot Options
Customize screenshot behavior with various options.
Full Page Screenshot
Mask Elements
Clip Region
Omit Background
import { test , expect } from '@playwright/test' ;
test ( 'capture full page' , async ({ page }) => {
await page . goto ( 'https://example.com' );
await expect ( page ). toHaveScreenshot ( 'full-page.png' , {
fullPage: true ,
});
});
Comparison Thresholds
Control the sensitivity of visual comparisons.
Pixel Threshold
Percentage Threshold
Pixel Color Threshold
import { test , expect } from '@playwright/test' ;
test ( 'allow minor differences' , async ({ page }) => {
await page . goto ( 'https://example.com' );
await expect ( page ). toHaveScreenshot ( 'homepage.png' , {
// Allow up to 100 pixels to differ
maxDiffPixels: 100 ,
});
});
Animation Handling
Handle animations and transitions in screenshots.
Disable Animations
Wait for Animations
Allow CSS Animations
import { test , expect } from '@playwright/test' ;
test ( 'screenshot without animations' , async ({ page }) => {
await page . goto ( 'https://example.com' );
await expect ( page ). toHaveScreenshot ( 'static-page.png' , {
animations: 'disabled' ,
});
});
Multi-Page Screenshots
Compare screenshots across different page states.
import { test , expect } from '@playwright/test' ;
test ( 'compare different states' , async ({ page }) => {
await page . goto ( 'https://example.com' );
// Initial state
await expect ( page ). toHaveScreenshot ( 'state-initial.png' );
// Click button to change state
await page . click ( 'button.toggle' );
await expect ( page ). toHaveScreenshot ( 'state-toggled.png' );
// Hover state
await page . hover ( '.card' );
await expect ( page ). toHaveScreenshot ( 'state-hover.png' );
});
Viewport Variations
Test visual consistency across different viewport sizes.
Multiple Viewports
Responsive Screenshots
import { test , expect , devices } from '@playwright/test' ;
const viewports = [
{ name: 'mobile' , ... devices [ 'iPhone 13' ] },
{ name: 'tablet' , width: 768 , height: 1024 },
{ name: 'desktop' , width: 1920 , height: 1080 },
];
for ( const viewport of viewports ) {
test ( `visual test ${ viewport . name } ` , async ({ browser }) => {
const context = await browser . newContext ({
viewport: {
width: viewport . width ,
height: viewport . height
},
});
const page = await context . newPage ();
await page . goto ( 'https://example.com' );
await expect ( page ). toHaveScreenshot ( `homepage- ${ viewport . name } .png` );
await context . close ();
});
}
Dark Mode Testing
Compare screenshots in both light and dark themes.
import { test , expect } from '@playwright/test' ;
test ( 'compare light and dark themes' , async ({ browser }) => {
// Light mode
const lightContext = await browser . newContext ({
colorScheme: 'light' ,
});
const lightPage = await lightContext . newPage ();
await lightPage . goto ( 'https://example.com' );
await expect ( lightPage ). toHaveScreenshot ( 'light-theme.png' );
await lightContext . close ();
// Dark mode
const darkContext = await browser . newContext ({
colorScheme: 'dark' ,
});
const darkPage = await darkContext . newPage ();
await darkPage . goto ( 'https://example.com' );
await expect ( darkPage ). toHaveScreenshot ( 'dark-theme.png' );
await darkContext . close ();
});
Update Snapshots
Update baseline screenshots when changes are intentional.
Update All Snapshots
Update Specific Test
Update in CI
# Update all snapshots
pnpx playwright test --update-snapshots
Cross-Browser Screenshots
Ensure visual consistency across different browsers.
import { defineConfig , devices } from '@playwright/test' ;
export default defineConfig ({
projects: [
{
name: 'chromium' ,
use: { ... devices [ 'Desktop Chrome' ] },
},
{
name: 'firefox' ,
use: { ... devices [ 'Desktop Firefox' ] },
},
{
name: 'webkit' ,
use: { ... devices [ 'Desktop Safari' ] },
},
] ,
// Each project will have its own set of snapshots
}) ;
Diff Output
Playwright generates diff images when screenshots don’t match.
Understanding Diff Images
// When a test fails, Playwright creates:
// 1. {name}-actual.png - Current screenshot
// 2. {name}-expected.png - Baseline screenshot
// 3. {name}-diff.png - Difference highlighted
// These are saved in:
// test-results/{test-name}/
Manual Screenshot Capture
Capture screenshots programmatically for custom comparisons.
Save Screenshot
Screenshot Buffer
Element Screenshot
import { test } from '@playwright/test' ;
import path from 'path' ;
test ( 'save custom screenshot' , async ({ page }) => {
await page . goto ( 'https://example.com' );
await page . screenshot ({
path: path . join ( 'screenshots' , 'custom.png' ),
fullPage: true ,
});
});
Configuration
Configure screenshot defaults globally.
import { defineConfig } from '@playwright/test' ;
export default defineConfig ({
expect: {
toHaveScreenshot: {
// Default settings for all screenshot assertions
maxDiffPixels: 100 ,
threshold: 0.2 ,
animations: 'disabled' ,
},
} ,
// Where to store snapshots
snapshotDir: './visual-snapshots' ,
// Snapshot path template
snapshotPathTemplate: '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{ext}' ,
}) ;
Best Practices
Mask Dynamic Content Always mask timestamps, user-specific data, and ads that change frequently.
Wait for Stability Ensure page is fully loaded and stable before taking screenshots. Wait for network idle.
Appropriate Thresholds Set reasonable thresholds - too strict causes false positives, too loose misses issues.
Organized Snapshots Use descriptive names and organize snapshots by feature or component.
Visual tests can be flaky due to font rendering differences across platforms. Consider running them in Docker or CI with consistent environments.
Screenshots Learn more about screenshot capabilities
Visual Debugging Debug visual test failures effectively