ScreenshotNeo

BlogHow-to

How to Take Website Screenshots with Playwright for QA Documentation

Capture reproducible Playwright screenshots for bug reports, test evidence, and visual regression checks, with runnable code and practical fixes.

By the ScreenshotNeo team4 October 20269 min read

Use Playwright’s page.screenshot() to save the current viewport, fullPage: true to capture the full scrollable page, and a locator’s screenshot() method to document one component. For repeatable visual regression checks, use Playwright Test’s toHaveScreenshot() and review its baseline. For QA evidence, first make the page state explicit, wait for the relevant UI condition, then capture a clearly named artifact.

1. Choose the right screenshot for the QA task

Need Playwright method What it gives you
One-time bug report or QA artifact page.screenshot({ path }) An image of the current page viewport; it does not compare against an expected image.
Long page documentation page.screenshot({ path, fullPage: true }) One tall image of the full scrollable page.
Component, alert, or validation state locator.screenshot({ path }) An image focused on the matched element.
Visual regression test expect(page).toHaveScreenshot() A comparison against a reviewed reference image, run with Playwright Test.
Evidence in a test report page.screenshot() plus step.attach() An image attached to a named test step for reporters.

Use the smallest capture that proves the issue. A viewport image preserves the surrounding context a tester sees; a locator image makes a small component easier to inspect; a full-page image is useful when the issue spans page sections. A screenshot alone is evidence of appearance at one point in time, not proof that the behavior is correct.

2. Install Playwright and create an artifact directory

The examples below use Playwright Test with TypeScript. Install the test package and browser binaries in the project:

npm init playwright@latest

Choose TypeScript when prompted, or install into an existing project with npm install -D @playwright/test followed by npx playwright install. Keep screenshot artifacts under a known directory so they are easy to attach to a bug report or inspect in CI. Create artifacts before writing files if your script does not create directories automatically.

mkdir -p artifacts

3. Capture a reproducible viewport screenshot

Navigate to the page, prepare any data and interaction that reveal the issue, and wait for a meaningful condition such as the alert becoming visible. Avoid relying on a fixed sleep when a locator assertion can identify the state directly.

import { test, expect } from '@playwright/test';

 test('capture checkout validation evidence', async ({ page }) => {
  await page.goto('https://example.com/checkout');
  await page.getByRole('button', { name: 'Place order' }).click();

  const alert = page.getByRole('alert');
  await expect(alert).toBeVisible();

  await page.screenshot({
    path: 'artifacts/checkout-validation.png',
    animations: 'disabled',
  });
});

Replace the example URL and action with the route and steps for your application. The test assumes the page has a button named “Place order” and exposes the validation message with the alert role; use locators that match the application’s actual accessible interface. The path option saves the image. PNG is the default image format.

4. Capture the full page or a single element

Full scrollable page

Use fullPage: true when the complete document is relevant to the evidence:

await page.screenshot({
  path: 'artifacts/pricing-page-full.png',
  fullPage: true,
  animations: 'disabled',
});

A full-page capture can become very tall on long pages. Prefer a viewport or component screenshot when the issue is local, since a focused artifact is easier to review and usually smaller.

One component

Use a locator screenshot to capture the matched element rather than an entire page:

const error = page.getByRole('alert');
await expect(error).toBeVisible();
await error.screenshot({ path: 'artifacts/checkout-error.png' });

Locator-based capture is the recommended element approach; Playwright marks the older ElementHandle screenshot method as discouraged. Ensure the locator resolves to the intended element and that it is visible before capture.

5. Attach screenshots to Playwright test reports

For test-step evidence, capture a buffer and attach it with a name and content type. This associates the image with the step in reporter output:

import { test, expect } from '@playwright/test';

test('checkout displays a validation error', async ({ page }) => {
  await page.goto('/checkout');
  await page.getByRole('button', { name: 'Place order' }).click();
  await expect(page.getByRole('alert')).toBeVisible();

  await test.step('capture validation state', async step => {
    const screenshot = await page.screenshot();
    await step.attach('checkout-validation', {
      body: screenshot,
      contentType: 'image/png',
    });
  });
});

The attachment API copies the image to a location reporters can access after the attachment completes. Configure your test reporter and inspect its output to find the resulting artifact.

6. Use visual assertions for regression checks

A saved screenshot documents one run. To compare the page with an expected visual state over time, use Playwright Test’s screenshot assertion:

import { test, expect } from '@playwright/test';

test('checkout matches its visual baseline', async ({ page }) => {
  await page.goto('/checkout');
  await expect(page.getByRole('heading', { name: 'Checkout' })).toBeVisible();
  await expect(page).toHaveScreenshot('checkout.png');
});

The assertion waits for two consecutive screenshots to match before comparing to the expectation. On the first run, Playwright creates a reference screenshot; review the image and add it to the repository as the intended baseline. When the UI changes intentionally, review the diff and update the snapshot using npx playwright test --update-snapshots. Do not update a baseline just to make an unexplained difference disappear.

Playwright Test supports PNG by default and WebP when the snapshot name uses the .webp extension. Its documentation describes both as lossless. Snapshot path configuration can keep references organized by test. Consult the documentation for the Playwright version pinned in your project because API options and defaults can change: visual comparisons and Page screenshot options.

7. Reduce noise without hiding the bug

  • Disable animations: direct screenshot capture allows animation by default. Set animations: 'disabled' when motion creates irrelevant variation. Finite animations are fast-forwarded and infinite animations are canceled for capture, then resumed. Screenshot assertions disable animations by default.
  • Mask dynamic areas: use locator masks for timestamps, avatars, or other volatile content that is not part of the behavior under review. A mask covers the matched element’s bounding box and is pink by default. Check that the masked area does not hide meaningful behavior.
  • Use screenshot styles narrowly: screenshot styles can hide or change volatile content during capture. Visual assertions also support stylePath. Limit these adjustments to known noise so the artifact remains an honest representation of the state.
  • Choose output scale intentionally: scale: 'css' produces one image pixel per CSS pixel; scale: 'device' captures device pixels. Device-pixel output can be larger, especially on high-density displays.

For direct screenshots, options such as fullPage, animations, and scale affect the captured artifact. Review the current Page screenshot API for the complete option list and version-specific defaults. A mask or style changes the evidence, so note that choice when the image is used to explain a defect.

8. Keep captures repeatable across machines

Browser rendering can differ with operating system, browser version, settings, hardware, power source, and headless mode. Generate and compare baselines in a consistent environment, including the same browser and operating system where practical. If the product deliberately supports multiple platforms, maintain separate expectations for those environments rather than treating all rendering differences as bugs.

  • Pin the Playwright version and install the matching browser binaries in CI.
  • Set viewport and device scale factor explicitly in the Playwright project configuration when they matter to the expected image.
  • Seed or reset test data and perform the same interactions before capture.
  • Wait for an observable state such as a locator becoming visible; reserve fixed delays for cases with no reliable signal.
  • Use meaningful artifact names that identify the route, state, and test or issue.
  • Review visual diffs before changing committed baselines.

Stable rendering makes visual comparisons more useful, but it does not make them self-approving: a changed baseline still needs review.

9. Complete runnable capture script

For a standalone Node.js script instead of a Playwright Test case, install Playwright and save the following as capture.mjs. It opens a page, waits for a visible heading, and writes a viewport screenshot. Replace the URL and heading with those for the page under review.

import { chromium } from 'playwright';
import { mkdir } from 'node:fs/promises';

const browser = await chromium.launch({ headless: true });
try {
  const page = await browser.newPage({
    viewport: { width: 1440, height: 900 },
    deviceScaleFactor: 1,
  });
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  await page.getByRole('heading').first().waitFor({ state: 'visible' });
  await mkdir('artifacts', { recursive: true });
  await page.screenshot({
    path: 'artifacts/example-page.png',
    animations: 'disabled',
  });
} finally {
  await browser.close();
}

Run it with node capture.mjs. This is a browser automation script, so it needs the Playwright package and a compatible browser installation. For authenticated or stateful pages, add the required sign-in or test data setup before capture and avoid putting real credentials in source control.

10. Troubleshooting

Symptom Likely cause Fix
Screenshot shows the loading state Capture ran before the relevant UI state was ready. Wait for a locator or application condition that identifies the finished state. Do not use an arbitrary sleep if a condition is available.
Locator screenshot times out or finds nothing The selector does not match, the element is not rendered, or the expected state was never reached. Check the accessible role/name or selector, perform the required interaction, then assert visibility before capture.
Image differs between local and CI Browser, OS, headless mode, scale, or hardware rendering differs. Align the environments and configuration, or use platform-specific baselines when platform differences are intentional.
Screenshot changes on every run Animation or dynamic content is included. Disable irrelevant animations; mask or normalize only known volatile regions, while preserving behavior being documented.
Full-page capture is unexpectedly large The document is long or device-pixel scale creates a larger image. Use viewport or locator capture if it communicates the issue; choose CSS scale when device pixels are unnecessary.
Snapshot assertion fails on first run No reference image exists yet. Review the generated reference screenshot and commit it if it represents the intended state.
Snapshot assertion fails after a UI change The page differs from the reviewed baseline, whether intentionally or unexpectedly. Inspect the diff; fix regressions or update the baseline only after confirming the change is expected.
Output file cannot be written The target directory does not exist or the process lacks write access. Create the directory before capture and check the working directory and permissions.

11. Performance, reliability, and cost

Screenshot capture requires page navigation, rendering, and image encoding, so the page state and asset loading usually determine how long the workflow takes. Capture only what the QA question needs: full-page images and device-pixel output can produce larger artifacts than a viewport or CSS-pixel image. Reuse the browser process across multiple captures in a test run where appropriate, and always close it in standalone scripts, including on errors.

Reliability comes from deterministic setup and explicit synchronization: stable test data, a pinned browser environment, locator-based waits, and reviewed baselines. A successful screenshot call only means an image was captured; it does not establish that the page loaded correctly. Assert the expected state before saving evidence. Playwright itself is software; this workflow does not require a paid screenshot API. Storage and CI artifact costs depend on your own setup, and no general cost figure applies.

12. Or skip the browser setup

For a URL-based screenshot without installing or running a browser, ScreenshotNeo accepts one GET request and returns an image or PDF. See the ScreenshotNeo API documentation for options.

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()
open("shot.webp", "wb").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 import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is available on every plan. Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month, with no card.

13. FAQ

Does a screenshot create a visual regression test?

No. A saved image is an artifact. Use toHaveScreenshot() with a reviewed reference image when you need an automated visual comparison.

Should I commit QA screenshots to the repository?

Commit visual reference images when they are maintained test baselines. For one-off bug evidence, attach the artifact to the issue or test report according to your team’s retention process.

Can screenshots include authenticated content?

Yes, if the browser context is authenticated before capture. Use test accounts or seeded state and keep credentials out of source files and committed artifacts.

Which image format should I use?

PNG is Playwright’s default. WebP is available for visual snapshot names ending in .webp; choose based on how the artifact will be reviewed and stored.

References