ScreenshotNeo

BlogHow-to

How to Test Email Templates with Browser Screenshot Comparisons

Use browser screenshots to catch visual regressions in email templates, then verify rendering in the email clients your audience uses.

By the ScreenshotNeo team4 October 20268 min read

Use browser screenshot comparisons to catch visual changes in an email template under a fixed browser setup. Then check previews in the email clients that matter to your audience: a browser screenshot is a regression check, not proof that an inbox will render the message the same way.

This guide uses Playwright Test for repeatable browser captures, explains how to review and maintain baselines, and covers the separate checks needed for real email-client rendering and behavior.

1. Decide what each kind of screenshot can tell you

A browser screenshot answers: “Did this template’s browser rendering change under the same conditions?” It is useful for catching unexpected spacing, color, typography, image, and responsive-layout changes.

An email-client preview answers a different question: “How does this message look in this specific inbox client?” Email clients can use different rendering engines and rules. Litmus distinguishes its Builder Browser pane, which uses the browser’s native rendering engine, from Email Previews, which capture static screenshots in selected email clients. Litmus also says Proof is a browser-rendered HTML view rather than a rendering in a specific client: Litmus Builder preview pane and Litmus Proof.

Use both checks when the template must work in inboxes. A passing Playwright comparison does not establish client compatibility.

2. Create a stable browser screenshot test

The example below serves a local HTML email and captures a full-page screenshot at a fixed viewport. It uses Playwright Test’s screenshot assertion so the first run can create a reference image and later runs can compare against it. Install the package and its browser as described in the Playwright visual comparisons documentation.

npm init -y
npm install --save-dev @playwright/test
npx playwright install chromium

Save an email fixture at fixtures/welcome.html. This minimal fixture is runnable and can be replaced with your real template and test data:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Welcome email fixture</title>
  <style>
    body { margin: 0; background: #f2f4f7; font: 16px Arial, sans-serif; }
    .email { box-sizing: border-box; width: 600px; max-width: 100%; margin: 24px auto; padding: 32px; background: white; color: #202938; }
    .button { display: inline-block; padding: 12px 18px; background: #185adb; color: white; text-decoration: none; }
  </style>
</head>
<body>
  <main class="email">
    <h1>Welcome, Alex</h1>
    <p>Your account is ready. Use the button below to get started.</p>
    <a class="button" href="https://example.com/account">Open your account</a>
  </main>
</body>
</html>

Create tests/email.spec.js:

const { test, expect } = require('@playwright/test');
const path = require('node:path');
const { pathToFileURL } = require('node:url');

test('welcome email matches its visual baseline', async ({ page }) => {
  await page.setViewportSize({ width: 640, height: 900 });
  const fileUrl = pathToFileURL(path.resolve('fixtures/welcome.html')).href;
  await page.goto(fileUrl);
  await page.evaluate(() => document.fonts.ready);
  await page.locator('img').evaluateAll(images => Promise.all(images.map(image => {
    if (image.complete) return Promise.resolve();
    return new Promise(resolve => {
      image.addEventListener('load', resolve, { once: true });
      image.addEventListener('error', resolve, { once: true });
    });
  })));
  await expect(page).toHaveScreenshot('welcome-email.png', {
    fullPage: true,
    animations: 'disabled'
  });
});

Run the test with npx playwright test tests/email.spec.js. Review the generated reference image before accepting it. On later runs, inspect any diff image and update the baseline only when the visual change is intentional. Playwright documents updating snapshots with npx playwright test --update-snapshots; treat that as a reviewed change rather than a way to silence a failing comparison.

3. Keep captures comparable

Screenshot comparisons are sensitive to their environment. Playwright warns that browser rendering can vary with the host operating system, browser version and settings, hardware, power source, headless mode, and other factors. Use the same environment for baseline generation and subsequent runs; ideally generate and compare snapshots in the same CI image.

  • Fix the inputs: use stable sample data, deterministic template variables, and known locale and timezone settings.
  • Fix the viewport: choose the email width you need to inspect, then add separate viewport cases for responsive layouts.
  • Wait for assets: wait for fonts and images to load. If your template fetches data, wait for a specific ready condition rather than an arbitrary short delay.
  • Reduce animation: disable animations or freeze animated content for a stable frame. A static screenshot cannot establish that an animated GIF plays correctly.
  • Keep browser versions aligned: install the same Playwright browser build in local development and CI.
  • Review baseline updates: compare the proposed baseline with the change being made and keep the image update alongside the template change.

For multiple viewport checks, parameterize the test with the widths your design supports. Do not interpret a single 600-pixel capture as evidence that narrow mobile layouts are correct.

4. Add real email-client previews

Choose client previews based on your audience and campaign requirements. Include the relevant desktop, mobile, and webmail clients rather than assuming one browser covers them. Litmus Previews & QA supports selecting clients, viewing full-length previews, and comparing them side by side; after edits, rerun or save the previews because the email-client screenshots do not update live. See the Litmus pre-send testing guide.

For a hosted email-preview workflow, Litmus email previews provide selected client captures and a comparison view. Email on Acid’s help article describes sending a message to client applications and returning stitched screenshots; it also identifies the current next-generation QA suite as Mailgun Inspect. Check the product’s current naming and capabilities before choosing a service: Email on Acid’s explanation of its email testing.

Compare previews side by side and record which clients were checked. A long email may require a full-length preview; a viewport-only browser capture can miss content farther down the message.

Images enabled and disabled

Review the email with images visible and with remote images blocked. This catches layouts that collapse when images are unavailable and meaningful content that exists only inside an image. For Litmus previews, test images need to be hosted on your server or email service provider and use absolute URLs. Litmus says CID and base64 embedded images do not render in iOS previews. See Litmus guidance on images in email testing.

A screenshot is visual evidence, not a functional test. Check that links have the intended destinations and that any required behavior works using separate tests or direct inspection. Email on Acid notes that its stitched JPG cannot be used to click links or preview animated GIFs. Verify link targets and animation separately.

6. Troubleshooting

Symptom Likely cause What to do
Many unrelated pixels differ on every run Browser, operating system, fonts, headless mode, or other rendering conditions changed. Run baseline and comparison in the same pinned environment and browser version.
Text moves or wraps inconsistently A web font is not ready, or a fallback font is being used. Wait for document.fonts.ready; verify the font URL loads and use the same font environment.
Images appear blank Remote image URLs are unavailable, relative, blocked, or not finished loading. Use publicly reachable absolute URLs for preview testing and wait for image load; also inspect an images-off preview.
Snapshot changes after a dependency update The browser build or rendering environment changed along with application code. Check the dependency and browser version change. Regenerate snapshots only after reviewing the intended rendering change.
Email-client preview looks different from browser baseline The two captures use different rendering targets. Treat this as a client compatibility issue to investigate; browser baseline success does not guarantee inbox fidelity.
Animated content or links cannot be verified in the capture Static screenshots do not expose playback or click behavior. Test animation and destinations separately in an appropriate client or functional check.
Preview does not reflect the latest edit A saved client preview may be a static capture from before the change. Rerun or save the preview after editing.

7. Performance, reliability, and cost

Browser baselines are fast to rerun once the browser and fixture are available, but stability depends on keeping the rendering environment and inputs consistent. Waiting for actual fonts and images improves the usefulness of the capture; excessive fixed delays slow the suite without guaranteeing that the page is ready. Prefer explicit readiness conditions.

Email-client preview services add coverage across rendering targets, but the set of clients you inspect affects the time and cost of your review. Select clients for your readership and supported requirements. Keep the browser regression suite for frequent changes and run client previews at the review or release points where broader compatibility evidence is needed.

Or skip the browser setup

If the task is to capture a web page or a hosted HTML email preview, ScreenshotNeo can return an image with one GET request. See the ScreenshotNeo API documentation and the product site at ScreenshotNeo.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Replace the example URL with a publicly reachable preview URL. ScreenshotNeo removes cookie banners, popups, and chat widgets before capture. Bot checks, blank pages, failed loads, and cache hits are never billed, and the response identifies the page verdict and billing status. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does a passing browser screenshot test prove an email works in Gmail or Outlook?

No. It confirms the template matched its baseline in the configured browser environment. Use previews in the email clients relevant to your audience.

Should every baseline difference fail the build?

Unexpected differences should be reviewed. Accept a new baseline only when the rendering change is intended and has been checked.

No. A screenshot shows appearance. Check destinations and interaction behavior separately.

Can ScreenshotNeo replace email-client previews?

No. It captures a page in a browser context; it does not simulate specific inbox clients. Use it for hosted-page captures, and use email-client previews for client-specific rendering evidence.