All articles
TestingPxShot

Building a Visual Regression Testing Pipeline with Screenshot APIs

February 8, 2026 9 min read

You ship a CSS fix on Friday. Monday morning, your designer notices the pricing page header is overlapping the navigation bar. The signup button on mobile is now invisible. A client emails about a broken checkout flow. None of your unit tests caught any of this — because they can't see what users see.

Visual regression testing solves this by comparing screenshots of your UI before and after every change. If something looks different, you get alerted before it reaches production. Here's how to set up a visual regression pipeline using a screenshot API.

What Is Visual Regression Testing?

Visual regression testing captures screenshots of your application's UI and compares them against known-good baselines. When a pixel difference exceeds your threshold, the test fails. This catches issues that functional tests miss:

  • CSS changes that break layouts on other pages
  • Font loading issues causing text shifts
  • Z-index problems hiding interactive elements
  • Responsive breakpoint bugs
  • Third-party widget changes affecting your UI

Architecture of a Visual Regression Pipeline

A typical visual regression pipeline has four stages:

📸

1. Capture

Take screenshots of every page/component at multiple viewport sizes

🔍

2. Compare

Diff the new screenshots against stored baselines pixel by pixel

🚨

3. Report

Flag differences that exceed the threshold, generate visual diff reports

4. Update

Accept intentional changes and update baseline images

Setting Up the Pipeline

Step 1: Define Your Test Pages

Start by listing the critical pages and viewport sizes you want to monitor:

// visual-tests/config.ts
export const testPages = [
  { name: "homepage", url: "/", viewports: [1440, 768, 375] },
  { name: "pricing", url: "/pricing", viewports: [1440, 768, 375] },
  { name: "signup", url: "/signup", viewports: [1440, 375] },
  { name: "dashboard", url: "/dashboard", viewports: [1440] },
];

Step 2: Capture Screenshots via API

Use a screenshot API to capture each page at each viewport. With PxShot, you can specify exact viewport dimensions and wait for the page to fully render:

// visual-tests/capture.ts
async function captureScreenshot(url: string, width: number) {
  const response = await fetch("https://pxshot.dev/api/screenshot", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer YOUR_API_KEY"
    },
    body: JSON.stringify({
      url: `https://staging.yoursite.com${url}`,
      width,
      height: 900,
      fullPage: true,
      format: "png"
    })
  });

  return Buffer.from(await response.arrayBuffer());
}

// Capture all test pages
for (const page of testPages) {
  for (const viewport of page.viewports) {
    const screenshot = await captureScreenshot(page.url, viewport);
    await fs.writeFile(
      `screenshots/${page.name}-${viewport}.png`,
      screenshot
    );
  }
}

Step 3: Compare Against Baselines

Use a pixel comparison library like pixelmatch to diff the new screenshots against your stored baselines:

import pixelmatch from "pixelmatch";
import { PNG } from "pngjs";

function compareImages(baseline: Buffer, current: Buffer) {
  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 }
  );

  const totalPixels = img1.width * img1.height;
  const diffPercentage = (mismatchedPixels / totalPixels) * 100;

  return { mismatchedPixels, diffPercentage, diffImage: diff };
}

Step 4: Integrate with CI/CD

Add the visual regression check to your GitHub Actions or CI pipeline so it runs on every pull request:

# .github/workflows/visual-regression.yml
name: Visual Regression Tests
on: [pull_request]

jobs:
  visual-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run visual-test
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: visual-diffs
          path: screenshots/diffs/

Best Practices

  • Set a reasonable threshold. A 0.1% pixel difference threshold avoids false positives from anti-aliasing and font rendering variations.
  • Test at multiple viewports. A desktop-only test misses mobile bugs, which are often the most impactful.
  • Mock dynamic content. Timestamps, ads, and user-specific data cause false failures. Use consistent test data.
  • Review diffs, don't auto-reject. Some changes are intentional. Make it easy to approve updates to baselines.
  • Keep baselines in version control. Store baseline images in Git (or Git LFS for large repos) so every team member works from the same reference.

Why Use a Cloud Screenshot API?

You could run Puppeteer or Playwright locally, but a cloud-based screenshot API offers advantages:

  • Consistency: Same rendering environment every time — no "works on my machine"
  • Speed: Parallel captures across edge nodes instead of sequential local rendering
  • No infrastructure: No need to manage headless Chrome in your CI environment
  • Reliability: No flaky timeouts or memory issues from running browsers in CI

Automate Your Visual Tests with PxShot

PxShot provides fast, consistent screenshots from Cloudflare's edge network. Capture full pages, specific elements, or custom HTML at any viewport size. The free tier includes 100 screenshots per month — enough for a solid visual regression pipeline.

Try PxShot Free →

Need help setting up visual regression testing or CI/CD pipelines? Talk to us — we build custom testing and automation solutions.

visual regressiontestingscreenshot apici/cdquality assurance