Back to blog
TestingCI/CDQuality
Visual Regression Testing: How to Compare Screenshots Across Deployments
Catch UI bugs before users do. Learn how to set up automated visual regression testing using screenshot comparison in your CI/CD pipeline.
2025-03-189 min read
Visual regression testing catches the bugs that unit tests miss — broken layouts, CSS regressions, overlapping elements, and styling issues that only become visible in the rendered page. By comparing screenshots between deployments, you can detect unintended visual changes before they reach production.
How Visual Regression Testing Works
- Baseline capture — Take screenshots of key pages in your known-good state
- Comparison capture — Take the same screenshots after code changes
- Pixel diff — Compare the two sets and highlight differences
- Review — A human reviews the diff to approve or reject changes
Setting Up with a Screenshot API
Step 1: Define Your Critical Pages
const pages = [
{ name: 'home', url: '/' },
{ name: 'pricing', url: '/pricing' },
{ name: 'docs', url: '/docs' },
{ name: 'dashboard-empty', url: '/dashboard' },
{ name: 'blog-listing', url: '/blog' },
];
Step 2: Capture Screenshots in CI
async function captureBaselines(baseUrl: string) {
for (const page of pages) {
const url = encodeURIComponent(baseUrl + page.url);
const res = await fetch(
`https://pxshot.dev/api/capture?url=${url}&width=1280&height=800&format=png&api_key=${API_KEY}`
);
const buffer = await res.arrayBuffer();
await fs.writeFile(`screenshots/${page.name}.png`, Buffer.from(buffer));
}
}
Step 3: Compare with Baselines
import pixelmatch from 'pixelmatch';
import { PNG } from 'pngjs';
function compareScreenshots(baseline: Buffer, current: Buffer): number {
const img1 = PNG.sync.read(baseline);
const img2 = PNG.sync.read(current);
const diff = new PNG({ width: img1.width, height: img1.height });
const mismatchedPixels = pixelmatch(
img1.data, img2.data, diff.data,
img1.width, img1.height, { threshold: 0.1 }
);
return mismatchedPixels / (img1.width * img1.height);
}
Step 4: Fail the Build on Unexpected Changes
const diffPercentage = compareScreenshots(baseline, current);
if (diffPercentage > 0.01) {
console.error(`Visual regression detected: ${(diffPercentage * 100).toFixed(2)}% change`);
process.exit(1);
}
Best Practices
- Consistent viewport — Always use the same width and height
- Disable animations — Inject CSS to turn off transitions during capture
- Mock dynamic content — Replace dates and user-specific data with static content
- Multiple breakpoints — Test mobile (375px), tablet (768px), and desktop (1280px)
- Threshold tuning — Anti-aliasing differences are normal; set threshold to ~0.1
PxShot's API makes it easy to capture consistent screenshots across environments. See the API docs for all available parameters.