How to use Playwright screenshot snapshots with a monorepo
Configure Playwright screenshot baselines for a monorepo, prevent collisions across packages and projects, and debug visual diffs in CI.
Use Playwright Test’s toHaveScreenshot() assertion for visual snapshots, and configure snapshotPathTemplate so each baseline path preserves the package, test, and—when needed—project identity. Commit the baseline images, generate and review updates deliberately, and keep the browser environment stable between baseline creation and CI comparison.
1. Add a visual screenshot assertion
Install Playwright Test and its browser binaries using the setup appropriate for your repository. A page-level assertion captures the page; a locator-level assertion is useful when only a component or region should be compared.
import { test, expect } from '@playwright/test';
test('landing page matches its visual baseline', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('landing.png');
});
test('navigation component matches its baseline', async ({ page }) => {
await page.goto('/');
await expect(page.locator('[data-testid="main-navigation"]'))
.toHaveScreenshot('main-navigation.png');
});
On the first run, Playwright creates a reference image. Later runs capture the current rendering and compare it with that reference. A failed comparison should be reviewed as a potential product change or a rendering instability, not automatically accepted.
2. Choose a baseline layout that fits the monorepo
Playwright Test’s snapshotPathTemplate controls where generated snapshots go. Its tokens include {testDir}, {testFileDir}, {testFilePath}, {testFileName}, {testName}, {arg}, {ext}, {platform}, {projectName}, and {snapshotDir}. Template paths resolve relative to the configuration directory. See the Playwright snapshotPathTemplate reference.
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
snapshotPathTemplate:
'{testDir}/__screenshots__{/projectName}/{testFilePath}/{arg}{ext}',
});
This places snapshots in a shared tree under the configured test directory, retaining the test file path and assertion argument. The optional slash before {projectName} is included only when the token has a value, so an unnamed project does not create an empty directory segment.
Common monorepo layouts
| Layout | When it fits | Path guidance |
|---|---|---|
| Shared root baseline tree | One configuration runs visual tests across packages and reviewers want all baselines together. | Keep {testFilePath} or another package-distinguishing component in the path. Check the resolved paths so same-named tests in different packages cannot collide. |
| Project-separated baselines | Projects intentionally cover different browsers, devices, or environments that produce distinct expected images. | Include {projectName}, for example with {/projectName}, to give each project a separate baseline namespace. |
| Package-local configurations | Packages own their tests and Playwright configurations independently. | Preserve the package boundary through the config location or template. Test paths are interpreted relative to the configured test directory; template paths resolve from the config directory. |
There is no universal monorepo directory convention. Pick a layout that matches package ownership and review practices, then confirm the actual generated paths in your repository before adopting it broadly.
3. Separate only the baselines that should differ
A Playwright project is a logical group of tests with its own configuration. Projects can represent different browsers, devices, or other settings. Treat these as comparison dimensions:
- Package and test: retain enough test path information to distinguish tests in different packages.
- Browser or device: use separate project identity when the rendering is intentionally different and needs its own expected image.
- Environment: separate snapshots if environments intentionally render different UI; otherwise keep the baseline policy consistent.
Every extra baseline set requires storage and review. Avoid duplicating snapshots for configurations that are meant to produce equivalent output, but do not force different expected renderings to share one file.
4. Create, review, and commit references
- Run the visual tests to create missing references.
- Inspect the new files and confirm their paths distinguish the intended package and project.
- Commit the snapshot directory with the tests. Playwright recommends keeping snapshots in version control and reviewing them.
- For an intentional visual change, run
npx playwright test --update-snapshots. - Review the resulting image changes, keep the expected updates, and commit them alongside the UI change.
Updating snapshots changes the expected output; it does not establish that a UI change is correct. Review what changed before accepting the new baseline.
5. Keep CI comparisons reproducible
Screenshot output can vary with host operating system, browser version, browser settings, hardware, power source, and headless mode. Create and compare baselines in a stable environment, and use the same setup for local baseline generation and CI where practical. See Playwright’s visual comparisons guide.
Playwright waits for consecutive screenshots to match when creating a reference, but this does not remove all environmental variation. For dynamic regions, use screenshot styling or masks only for known volatile content. Set tolerances such as maxDiffPixels according to an explicit acceptance policy; do not use them to hide broad or unexplained changes.
6. Troubleshoot path and visual failures
| Symptom | Likely cause | What to do |
|---|---|---|
| One package overwrites or reuses another package’s baseline | The template omits a package-distinguishing test path, or tests resolve to the same template identity. | Preserve {testFilePath} or another unique package path component. Inspect generated locations and adjust the template. |
| Different projects report the same expected image | The project name is absent from the path even though projects intentionally render differently. | Add {projectName} to snapshotPathTemplate and regenerate only after verifying the intended project outputs. |
| Snapshot path is unexpected | Template paths resolve from the config directory, while test file tokens are interpreted in the configured test-directory context. | Check the configuration location, testDir, and the selected tokens together; run a focused test and inspect the created path. |
| CI has visual diffs that do not appear locally | Operating system, browser version, rendering settings, hardware, power state, or headless mode differs. | Align the comparison environment and browser setup; inspect actual and expected images before changing the baseline. |
| Small diff appears in a timestamp, animation, or changing content | The capture includes known volatile content. | Make test data deterministic where possible. Mask or style only the specific unstable region, and preserve checks for surrounding UI. |
| A large change passes after increasing a tolerance | The tolerance may be too broad and can hide a real regression. | Review the diff and use a tolerance that reflects a documented, narrow acceptance policy. |
When a comparison fails, inspect the expected image, actual image, and diff image. Playwright Trace Viewer action screenshots and DOM snapshots can help identify which page state produced the mismatch; see the Trace Viewer documentation.
7. Performance, reliability, and maintenance
- Run scope: focus on changed packages or tests during local iteration when your workflow permits, then retain the intended full visual suite in CI. Avoid running every browser and device project unless those configurations need coverage.
- Baseline size: project separation can multiply image count. Keep only comparison dimensions that represent meaningful differences.
- Review cost: snapshots are source-controlled artifacts. Group them with the code that changes their appearance so reviewers can understand why the baseline moved.
- Reliability: stable browser and host environments reduce noise. Deterministic content, narrow masks, and reviewed thresholds make failures more actionable.
- Cost: native Playwright snapshots use repository storage and CI execution resources. Hosted visual testing services may add separate service costs and operational requirements; assess their pricing and access controls directly before adoption.
8. Hosted review options
Native Playwright snapshots keep references in the repository and use normal code review. Teams that need hosted visual review can also evaluate documented integrations such as Chromatic’s Playwright integration and Percy’s Playwright client. Compare where baselines are stored, how reviewers approve changes, CI integration, access controls, and supported Playwright versions against your project’s requirements. Vendor documentation describes their integrations; it does not establish independent performance superiority.
Or skip the browser setup
If you need screenshots of live pages rather than repository-managed test baselines, ScreenshotNeo is a website screenshot API and MCP server. It can return a PNG, JPEG, WebP, or PDF from one request. See the ScreenshotNeo API documentation.
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', bytes));
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents. - The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
Should snapshots be checked into Git?
Yes. Commit the reference directory with the tests so changes can be reviewed alongside source changes.
Do all Playwright projects need separate baselines?
Only when their expected renderings differ. Include project identity in the template when distinct configurations need distinct references.
Can Playwright snapshots replace hosted visual review?
They provide repository-based comparison and review. Hosted integrations are an optional workflow for teams with requirements those services meet.


