ScreenshotNeo

BlogComparisons

Best Playwright Screenshot Libraries for Node.js

Compare Playwright’s built-in screenshot tools and hosted options, with runnable Node.js examples for capture, visual regression, and stable baselines.

By the ScreenshotNeo team4 October 20269 min read

For a Node.js project already using Playwright Test, start with its built-in expect(page).toHaveScreenshot() assertion for visual regression. It creates a reference image on the first run and compares later runs against it. If you only need to capture an image, use Playwright’s page.screenshot() API; add a separate comparison workflow if you need custom diffs or storage. For hosted visual-testing workflows, evaluate Percy and Applitools against your requirements. If you need a screenshot API rather than a test-runner library, ScreenshotNeo is the first alternative to try: it removes common consent banners, popups, and chat widgets before capture, and bills only clean shots.

What “screenshot library” means in a Playwright project

There are two different jobs developers often group under screenshot libraries:

  • Capture: Render a page or element and save or return an image. Playwright provides this through page.screenshot().
  • Visual comparison: Compare the current rendering with an approved reference image and report differences. Playwright Test provides this through expect(page).toHaveScreenshot().

Playwright’s screenshot assertion is a feature of the Playwright Test runner. If you use another test runner, you can still launch Playwright and capture screenshots, but you will need to choose or build the baseline storage, comparison, update review, and CI reporting workflow separately.

Option Best fit What it provides What to plan for
Playwright Test screenshot assertion Playwright Test projects that need visual regression checks Baseline creation and matching through toHaveScreenshot() Keep rendering environments consistent; review intentional baseline updates
Playwright page.screenshot() Capture-only tasks or custom image pipelines Image file or buffer; full-page and element capture Comparison, baseline storage, and diff reporting are separate choices
Percy for Playwright Teams assessing a hosted visual-testing workflow A Playwright integration is documented on its package page Check current support, workflow, plan limits, pricing, and data handling with the vendor
Applitools visual testing Teams assessing a hosted visual-testing workflow Applitools lists Playwright among supported frameworks This compatibility claim is vendor-provided; verify current terms and capabilities directly
ScreenshotNeo screenshot API Apps, scripts, and agents that need screenshots without managing a browser One GET request returns PNG, JPEG, WebP, or PDF; an MCP server provides screenshot tools to AI agents It is a capture API, not a Playwright Test baseline assertion

For Percy, see the @percy/playwright package. Applitools’ own visual testing tools comparison lists Playwright; treat that as a vendor claim rather than independent testing. The available research does not establish which hosted service is best or verify current prices, program terms, or detailed limits.

Use Playwright Test for visual regression

This is the recommended starting point when the project already runs Playwright Test. The first run creates a snapshot baseline; subsequent runs compare against it.

Install and create a visual test

npm install --save-dev @playwright/test
npx playwright install

Create tests/homepage.spec.js:

const { test, expect } = require('@playwright/test');

test('homepage visual snapshot', async ({ page }) => {
  await page.goto('http://localhost:3000');
  await expect(page).toHaveScreenshot('homepage.png');
});

Run the test:

npx playwright test tests/homepage.spec.js

On its first run, the assertion generates the reference screenshot. Review and commit the generated baseline with the test. Later runs compare the page with that reference. Playwright’s visual-comparison guide says the assertion takes screenshots until two consecutive screenshots match before saving, which helps avoid capturing a changing frame.

Control expected visual differences

Small rendering differences can be tolerated with assertion options. A stylesheet can hide or stabilize content that changes on every run, such as timestamps or animated elements. Use these controls narrowly: masking or broad tolerances can hide a real regression.

const { test, expect } = require('@playwright/test');

test('stable homepage snapshot', async ({ page }) => {
  await page.goto('http://localhost:3000');

  await expect(page).toHaveScreenshot('homepage.png', {
    animations: 'disabled',
    maxDiffPixelRatio: 0.01,
    stylePath: 'tests/screenshot-stability.css',
  });
});

Example tests/screenshot-stability.css:

/* Hide content whose value changes between otherwise identical runs. */
.clock,
.live-status-timestamp {
  visibility: hidden !important;
}

Choose an appropriate tolerance for the project rather than copying the sample value blindly. Playwright’s snapshot assertion API documents options including pixel-difference thresholds. For the full option set and runner behavior, consult the visual comparisons guide and SnapshotAssertions API.

Review and update baselines deliberately

When a UI change is intentional, use Playwright Test’s snapshot update mode, inspect the changed images, and commit approved baselines with the code change:

npx playwright test --update-snapshots

Baseline images are test artifacts that describe accepted appearance. Reviewing their diffs in code review makes accidental visual changes easier to catch.

Capture screenshots directly with Playwright

Use page.screenshot() when you need an image for a report, a downstream image processor, or a comparison system other than Playwright Test. It can write to a file or return an image buffer, capture the full page, and target an element.

Runnable Node.js capture example

Install Playwright if it is not already in the project:

npm install playwright
npx playwright install chromium

Save as capture.js and run with node capture.js:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });
  try {
    const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
    await page.goto('https://example.com', { waitUntil: 'networkidle' });

    // Capture the visible viewport to a file.
    await page.screenshot({ path: 'viewport.png' });

    // Capture the whole document, including content below the fold.
    await page.screenshot({ path: 'full-page.png', fullPage: true });

    // Capture only a selected element.
    const card = page.locator('main');
    await card.screenshot({ path: 'main.png' });

    // Return image bytes when another library or service will process them.
    const imageBuffer = await page.screenshot({ type: 'jpeg', quality: 85 });
    console.log(`Captured ${imageBuffer.length} bytes`);
  } finally {
    await browser.close();
  }
})().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

For capture options and additional examples, use the Playwright screenshots documentation. PNG is the default screenshot format; JPEG supports a quality setting. Choose a file path or buffer based on the next step in your pipeline.

Use cURL, Python, or Node.js with a screenshot API

These examples call ScreenshotNeo’s website screenshot API directly. They are useful when the job is to obtain a rendered image from a script or service and you do not need a local Playwright browser or a Playwright Test baseline assertion. See the ScreenshotNeo API documentation for parameters and response details.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as image:
    image.write(r.content)

Node.js

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} ${await res.text()}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request can return a PNG, JPEG, WebP, or PDF. 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, failed loads, timeouts, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives Claude, Cursor, and other MCP clients the take_screenshot, get_page_info, and capture_pdf tools.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

There are 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000. Every feature is on every plan. Read the API docs and sign up for 1,000 free screenshots a month, with no card.

Choosing a workflow and keeping screenshots stable

  1. Decide whether you need capture or regression detection. For approved visual baselines in Playwright Test, use toHaveScreenshot(). For an image artifact only, use page.screenshot() or a screenshot API.
  2. Keep baseline and test environments aligned. Use the same operating system, browser version, browser settings, viewport, and headless mode for baseline generation and CI runs where possible.
  3. Remove sources of nondeterminism. Disable animations, wait for application data to settle, and hide or mask volatile content only when it is irrelevant to the visual check.
  4. Make baseline updates reviewable. Update snapshots deliberately and inspect the resulting image changes before accepting them.
  5. Evaluate hosted services against current requirements. Verify supported browsers, review workflow, integrations, plan limits, pricing, and data handling directly with Percy or Applitools; the cited research does not verify those details.

Rendering can change with the host OS, browser version, settings, hardware, power source, and headless mode. This is why a baseline made on a developer laptop can differ from one generated in CI. Treat environment consistency as part of the screenshot test setup, not as an afterthought.

Performance, reliability, and cost

  • Performance: Browser startup, navigation, page readiness, and image encoding all contribute to capture time. Reuse a browser process across multiple captures in a script when practical, and close it in a finally block. Full-page captures can produce larger images and take longer than viewport captures.
  • Reliability: A successful navigation event does not guarantee that dynamic content has settled. Wait for the relevant app state or selector before capture, and use consistent browser and host environments for visual assertions. Avoid relying on a fixed delay when a meaningful readiness condition is available.
  • Cost: Playwright is an open-source browser automation library, but running browsers still uses developer or CI compute and storage. Hosted visual-testing service pricing and limits were not established by the research; check each vendor’s current terms. ScreenshotNeo has a free tier of 1,000 shots/month and paid plans from $5 for 3,000; yearly billing gives two months free.

Troubleshooting

Symptom Likely cause Fix
toHaveScreenshot is unavailable or the test does not recognize it The test is not running with Playwright Test, or the assertion API is not imported from its runner. Use @playwright/test and import test and expect from it. Snapshot matching is a Playwright Test runner feature.
Snapshots differ on CI but look unchanged locally Host OS, browser version, settings, hardware, or headless mode differs. Align the baseline and CI environments and pin the browser/runtime setup used by the project.
Images differ on every run Animations, clocks, rotating content, asynchronous data, or other volatile elements are visible. Wait for the app to settle; disable animations or use a targeted stylesheet to hide irrelevant changing content.
A whole-page screenshot omits content or has unexpected layout Lazy-loaded sections may not have rendered before capture, or full-page layout differs from the viewport. Scroll or otherwise trigger required content, wait for it to load, and inspect the page at the intended viewport before capture.
Element screenshot fails or captures the wrong region The locator matches no visible element, matches multiple elements, or the target is outside the expected state. Wait for the intended locator to be visible and make the locator specific before calling locator.screenshot().
Image comparison reports too many pixels changed A real UI change occurred, or rendering noise exceeds the configured threshold. Inspect the diff first. If the change is intentional, update the baseline; if it is noise, stabilize the source or tune a narrow tolerance.
API output is not a valid image The request may have returned an error response, or the target page may have failed to load. Check HTTP status and response headers before saving bytes; consult the API docs and inspect the page verdict and billing headers.

Frequently asked questions

Which Playwright option should a Node.js team start with?

Use expect(page).toHaveScreenshot() when the project uses Playwright Test and needs visual regression checks. Use page.screenshot() when you need capture without Playwright-managed baselines.

Does page.screenshot() compare against an earlier image?

No. It captures an image. A separate assertion or image-diff workflow must perform the comparison.

Can I use Playwright snapshots with another test runner?

The documented snapshot assertion is part of Playwright Test. With another runner, capture through Playwright and provide the comparison and baseline workflow separately.

Is pixelmatch itself a complete screenshot-testing solution?

No. Playwright’s visual-comparison documentation identifies pixelmatch as its comparison library, but a complete workflow also needs capture, baseline storage, update review, and CI integration.

When does a screenshot API fit better than a Playwright library?

Use an API when a script or application needs rendered images without managing browser installation and lifecycle. Use Playwright locally when you need browser automation or its test runner’s integrated snapshot assertions.