To create a seamless website UX, testers need to verify snapshots visually across multiple browsers.
With screenshot testing, you can catch visual bugs that functional tests miss, ensure only intended changes are made, and affirm cross-browser compatibility of your apps.
This guide explains how screenshot testing works, when to use it and what tools to use to capture screenshots. Let’s get started.
What is Screenshot Testing?
Screenshot testing is a form of visual testing where a tool captures an image of a UI; a full page, a single component or a specific state after a user action, to check responsiveness and visual clarity.
In screenshot testing, the tool captures the webpage’s visual and compares it pixel-by-pixel (or region-by-region against a saved reference image, usually called a baseline).
If the two images match within an acceptable tolerance, the test passes.
Screenshot testing is somewhere between functional testing and manual QA. A functional test can confirm that a submit button fires the right event. It has no idea if the button is rendering three pixels lower than it should or if a CSS alteration disturbed the footer.
For QA and SDET teams, it is less about replacing test cases and test suites and more about adding a layer that specifically checks visual appearance and layout so that nobody has to verify code again and again.
Original Navbar
Current Navbar
If you perform a website screenshot test for the above example, it will reflect a failed test based on changes in the navbars’ styling. The test will highlight if something has changed without stating which screenshot is correct.
Read More: Best Practices for Visual Testing
Types of Screenshot Testing
Not every screenshot test serves the same purpose. Depending on what you’re trying to validate, you’ll typically reach for one of these approaches:
- Visual regression testing: Compares screenshots of a page or component across builds to catch unintended changes. This is the most common use case and the one most people mean when they say “screenshot testing”.
- Cross-browser screenshot testing: Captures the same page across Chrome, Firefox, Safari, and Edge to confirm rendering stays consistent regardless of engine.
- Responsive screenshot testing: Checks how a layout holds up across screen sizes and orientations, which matters most for mobile-first or fluid designs.
- Component-level (isolated) screenshot testing: Captures individual UI components in isolation, often via a tool like Storybook, rather than a full page. Useful for design-system teams that want granular coverage without full-page noise.
- Manual screenshot testing: A tester manually captures and visually reviews screenshots. Slower and harder to scale, but still valuable for exploratory testing or areas where automated diffing struggles (animations, gradients, complex imagery).
- Dynamic-content-aware screenshot testing: Masks or excludes elements that change on every run, like timestamps, ads, or live data feeds, so they don’t trigger false failures.
Read More: How to find bugs in software?
When to Use Screenshot Testing?
Screenshot testing earns its place in the pipeline when visual accuracy is genuinely at risk, not on every single change. Reach for it if there are:
- Shipping layout, styling, or design updates.
- New features that change what the user sees, not just what the app does.
- Confirmed consistent renderings across browsers, devices, or viewports.
- Code refactorings that could affect layout: CSS restructuring, component library upgrades, and framework migrations.
- Automated visual gates in your CI/CD pipeline so that regressions get caught before merge, not after a user reports them.
It’s less useful as a stand-in for logic-heavy testing. If what you’re validating is business logic, data accuracy, or API behaviour, that still belongs in unit and integration tests; screenshot testing is a complement, not a replacement.
How Does Screenshot Testing Work?
The mechanics are consistent across most tools, even though the implementation details vary:
- Capture a baseline. The team takes a screenshot of the UI in its accepted, “correct” state. This becomes the reference point for every future comparison.
- Trigger the test flow. Automated tests run through the relevant user actions, page load, navigation, form interaction, and framework (Cypress or Selenium).
- Capture new screenshots. At each relevant checkpoint, the tool takes a fresh screenshot of the current UI state.
- Run image diffing. The new screenshot is compared against the baseline, usually pixel-by-pixel, with the tool highlighting any regions that differ.
- Flag and review. Differences get surfaced to the team. Someone has to make a judgement call: is this an intentional design change or a bug?
- Fix or approve. Bugs get fixed and re-tested. Intentional changes get approved.
- Update the baseline. Once a change is confirmed as intentional, the baseline is refreshed so it reflects the new expected state going forward.
That last step matters more than it sounds. Baselines that don’t get updated turn into a source of constant false failures, which is usually what causes teams to start ignoring screenshot test results altogether, which is the opposite of what you want.
#How Does Screenshot Testing Work?
import { test, expect } from '@playwright/test';
test('homepage visual test', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveScreenshot('homepage.png');
});Output –
# Component-Level Screenshot Testing
test('button component', async ({ page }) => {
await page.goto('http://localhost:3000/storybook');
const button = page.locator('[data-testid="primary-button"]');
await expect(button).toHaveScreenshot('primary-button.png');
});Output –
Advantages of Screenshot Testing
Here are the advantages of screenshot testing:
- Easy to maintain: Screenshot testing usually contains only a few lines of code for testing. Thus, it becomes easier to maintain.
- Consistency Across Devices: Ensures that the UI appears consistently across different screen sizes, resolutions, and browsers.
- Faster Feedback: Automated screenshot comparison provides quick feedback, allowing developers to spot and fix visual issues early in the development cycle.
- Improved UI Quality: It aids in maintaining a high standard of UI quality by catching small, hard-to-notice visual discrepancies like misaligned elements or color changes.
- No Manual Checking Required: Eliminates the need for manual visual checks, reducing human error and saving time.
- Easier Collaboration: Visual diffs highlight issues clearly, making it easier for teams (developers, designers, QA) to communicate about UI bugs and changes.
- Cross-Platform Compatibility: Helps confirm that the UI renders correctly across different operating systems, devices, and browsers, improving cross-platform consistency.
Disadvantages of Screenshot Testing
While screenshot testing effectively ensures visual consistency in web applications, it comes with specific challenges.
- High Maintenance Overhead: As the UI evolves, keeping the baseline images updated can become time-consuming. Any slight change can require re-capturing and updating the screenshots, which can be labor-intensive.
- False Positives/Negatives: Screenshot tests can sometimes fail due to minor changes, such as slight spacing adjustments, font rendering differences, or changes in dynamic content (like ads, timestamps), leading to unnecessary alerts or missing actual issues.
- Resource Intensive: Running screenshot tests requires significant resources, especially when testing across multiple browsers, devices, or screen sizes. This can increase build times in CI/CD pipelines.
- Difficult to Test Dynamic Content: Dynamic elements, such as animations, hover states, or user-specific content, are difficult to test with screenshot comparison, as their appearance changes frequently.
- Limited Coverage: Screenshot testing only checks the visual aspects of the UI. It doesn’t catch functional issues, accessibility problems, or performance-related bugs, requiring complementary testing approaches.
- Sensitive to Environment Variations: Differences in screen resolution, browser version, or operating system can lead to visual inconsistencies, making it hard to get accurate, reproducible results across environments.
Screenshot Testing: Trade-Offs Worth Knowing Before You Adopt It
Screenshot testing isn’t free, and going in with clear expectations saves a lot of frustration later.
- Flakiness is real. Font rendering differences, anti-aliasing, animations, and dynamic content are common sources of false positives. Tools generally offer masking or tolerance thresholds to manage this, but it needs deliberate setup; it doesn’t happen automatically.
- Maintenance has a cost. Every intentional UI test means someone needs to review and re-approve a new baseline. On a fast-moving product, this can turn into a recurring chore if it’s not owned clearly.
- It’s resource-intensive at scale. Running comparisons across many browsers, devices, and viewports adds real time to CI/CD pipelines, especially as your component count grows.
- It only tells you about appearance. Screenshot testing won’t catch broken logic, accessibility gaps, or performance regressions. It needs to sit alongside, not instead of, your functional, accessibility, and performance test suites.
- Environment drift affects reliability. OS-level font rendering, GPU differences, and browser version mismatches between a developer’s machine and CI can produce diffs that have nothing to do with the actual code change, which is why most teams standardise screenshot capture on a consistent server environment.
None of these are reasons to skip screenshot testing; they’re reasons to scope it deliberately, on the pages and components where visual accuracy actually matters, rather than everywhere by default.
#Dynamic-content-aware Screenshot Testing
import { test, expect } from '@playwright/test';
test('ignore dynamic timestamp', async ({ page }) => {
await page.goto('https://example.com/dashboard');
await expect(page.locator('body')).toHaveScreenshot('dashboard.png', {
mask: [page.locator('.timestamp')]
});
});Output –
Tools for Performing Screenshot Testing
The right tool depends on your stack, your CI setup, and how much visual coverage you actually need.
- BrowserStack Percy: A cloud-based visual testing platform that captures and compares screenshots across real browsers and devices, with AI-assisted noise reduction to cut down on false positives from dynamic content.
- Playwright: It has built-in screenshot comparison support, useful for teams already running Playwright for functional end-to-end tests and looking to add visual checks without introducing a separate framework.
- BackstopJS: An open-source visual regression tool that supports both desktop and mobile viewports, popular for teams that want a self-hosted setup.
- Applitools: Uses visual AI to compare screenshots with an emphasis on filtering out noise while still catching genuine layout shifts.
- Chromatic: Built specifically around Storybook, well suited for teams doing component-level visual testing in a design-system context.
- Wraith/Needle: Lighter open-source options for teams that want screenshot comparison without the overhead of a full platform, though they require more manual configuration around thresholds and masking.
Most QA and SDET teams land on a combination: a functional test framework (Playwright, Cypress, Selenium) driving the app to the right state, paired with a dedicated visual testing tool doing the actual image capture and diffing.
Screenshot Testing Using BrowserStack Percy
Percy is an advanced visual testing tool that automatically detects layout shifts, styling changes, and content updates across different screens and devices.
Percy uses AI to reduce noise, such as dynamic banners or text rendering issues, focusing on meaningful visual changes for faster reviews. It supports cross-browser testing, responsive design validation, and comparison of design mockups from tools like Figma, ensuring no visual regressions make it into production.
Here’s a step-by-step guide to running visual tests with Percy, starting with a simple HTML page:
Step 1: Install Percy CLI
First, you need to install the Percy CLI in your local environment. Run the following command to install it:
npm install @percy/cli
Step 2: Create a Percy Project
Create a new project by clicking on “Create new project,” naming your project, and following the setup instructions. After the project is created, a Percy Token will be generated. This token identifies your project builds.
Step 3: Set Environment Variables
To use Percy, you need to set up your environment variables. This token will be used to authenticate your builds. In your terminal (for local testing), export the token:
export PERCY_TOKEN="<your-token-here>" export PERCY_BRANCH="<your-git-branch>"
Step 4: Create Your First Snapshot
In your project folder, create a simple HTML file (index.html) with the following content to test Percy:
<!DOCTYPE html>
<html>
<head>
<title>Todo List</title>
<style>
body { font-family: system-ui; max-width: 500px; padding: 20px; margin: 20px auto; box-shadow: 0 10px 10px #eee; }
input[type="text"] { width: 100%; padding: 12px 20px; margin: 8px 0; border-radius: 4px; }
ul { list-style-type: none; margin: 0; padding: 0; }
li { margin: 8px 0; padding: 12px 20px; background-color: #995dbb; color: white; border-radius: 4px; }
li.done { opacity: 0.4; text-decoration: line-through; }
h1 { color: rgba(0,0,0,0.78); text-align: center; }
</style>
</head>
<body>
<h1>hello percy!</h1>
<input type="text" placeholder="What do you want to do?">
<ul>
<li>Wake Up</li>
<li>Exercise</li>
<li>Eat</li>
</ul>
</body>
</html>Step 5: Create Snapshot Configuration
To tell Percy to capture the screenshot of your page, create a snapshots.yml file:
serve: . snapshots: - name: Home Page url: /index.html
Step 6: Run the First Build
Now, run your first Percy snapshot command to capture a screenshot of your HTML page:
npx percy snapshot snapshots.yml
Step 7: Introduce Visual Changes
Make a change to your HTML, such as marking an item as done:
<li class="done">Exercise</li>
Step 8: Generate the Second Build
Run the snapshot command again to capture the updated screenshot:
npx percy snapshot snapshots.yml
Step 9: View Results
Percy will generate a comparison between the first and second builds. The results will be available on your Percy dashboard:
Percy has started! Snapshot taken: HelloPercy Finalized build #2: [Link to Percy results]
Step 10: Review and Approve Changes
In the Percy dashboard, review the visual comparison. If you’re satisfied with the changes, approve them. Percy highlights any layout differences to help you spot unintended visual regressions.
#Screenshot Testing Using BrowserStack Percy
import percySnapshot from '@percy/playwright';
test('homepage visual validation', async ({ page }) => {
await page.goto('https://example.com');
await percySnapshot(page, 'Homepage');
});Output –
Read More: Explaining Playwright Architecture in 2026
Best Practices Screenshot Testing for QA and SDET Teams
Screenshot testing detects regressions and ensures visual consistency across web applications. The following best practices help streamline the process for efficient and reliable results.
- Keep screenshot tests to a minimum: Focus on critical UI components that are more likely to break, such as headers, footers, and navigation. Avoid excessive screenshot tests to reduce maintenance overhead.
- Take screenshots on a server: Run tests in a consistent server environment to eliminate discrepancies caused by local setups, such as different OS or browser versions.
- Use a consistent viewport and test environment: Test with a fixed screen size, the same browser version, and consistent configurations to minimize visual differences caused by varying environments.
- Maintain a baseline for comparison: Use a baseline image to compare each UI element. Update the baseline only after reviewing intentional changes to prevent errors.
- Avoid dynamic or frequently changing elements: Exclude elements like timestamps, ads, or user-specific data that change often and can trigger false positives in tests.
- Handle false positives and visual noise: Set tolerance thresholds for small, non-impactful differences. Exclude non-critical elements that cause visual noise, such as ads or dynamic content.
Also Read: Best Practices for Visual Testing
#CI/CD Example - name: Run Playwright Tests run: npx playwright test - name: Run Percy Visual Tests run: npx percy exec -- npx playwright test
Output –
Conclusion
Screenshot testing fills a gap that functional tests were never built to cover: whether the application actually looks right, not just whether it behaves correctly.
For QA and SDET teams, it is most valuable when scoped differently, around the components and flows where visual accuracy is non-negotiable.
Used well, it turns visual QA from a manual, easy-to-skip step into a repeatable check that runs on every build, catching the kind of bugs that disrupt the visual viewport






