Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Playwright’s Page API: launch a browser, open a page, navigate to the target URL, then call await page.screenshot({ path: 'screenshot.png' }). The file is written when the promise resolves. Add fullPage: true for the entire scrollable page, omit path to receive an image Buffer, or call screenshot() on a locator to capture one element.

The smallest working Node.js example

This CommonJS script captures the visible viewport of https://example.com and saves it as screenshot.png. It assumes Playwright and the selected browser have already been installed in your project.

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

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: 'screenshot.png' });
  await browser.close();
})();

Replace chromium with firefox or webkit when you need to capture in another browser engine. Keep the browser close in a finally block in production code so a navigation or screenshot error does not leave a process running.

Prepare the project without hard-coding stale versions

Add Playwright to your Node.js project with the package manager and browser-install steps documented for the Playwright release you are using. Browser binaries and supported Node.js versions can change, so verify those details against the current Playwright setup guidance rather than copying an old version pin. The code above is deliberately independent of a particular Playwright version.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run the script from the directory in which you want the image. A relative path is resolved from Node’s current working directory, not from the JavaScript file’s directory. Create the destination directory first if it does not exist.

Viewport, full-page, and element screenshots

Capture the current viewport

page.screenshot() captures what is visible in the page viewport by default. This is the right choice for a browser-like snapshot at a known width and height.

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

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
  await page.goto('https://example.com');
  await page.screenshot({ path: 'viewport.png' });
  await browser.close();
})();

Capture the complete scrollable page

Set fullPage: true when the output should include content below the fold.

await page.screenshot({
  path: 'full-page.png',
  fullPage: true
});

The viewport width remains the width you configured; Playwright extends the capture vertically to include the page’s scrollable content. Very tall pages produce large images, so use a viewport capture when a single screen is all you need.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture one element

Use a locator instead of the discouraged ElementHandle screenshot API. Locator screenshots wait for the target to be actionable and scroll it into view before capturing it.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
const card = page.locator('.pricing-card');
await card.screenshot({ path: 'pricing-card.png' });

The element must exist and be visible in the rendered page. If the target is inside a scrollable container, the screenshot contains the portion currently exposed by that container; it does not automatically turn the container into an infinite full-page image.

Save an image file or keep the image in memory

Write directly to disk

Pass path to save the result. The output format is inferred from the filename extension, so use .png, .jpg or .jpeg, or .webp when that format is supported by your Playwright version.

await page.screenshot({ path: 'artifacts/home.webp' });

Return a Buffer

Omit path when another Node.js API should receive the bytes, such as an object-storage client, a test attachment, or an image-processing library.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const image = await page.screenshot({ type: 'png' });
console.log(`captured ${image.length} bytes`);

A returned Buffer avoids an intermediate file. If you need a file later, write it with Node’s filesystem API after the screenshot promise resolves.

Choose format, quality, pixel scale, and background

PNG, JPEG, and WebP

PNG is the default. JPEG and WebP can be selected with type. The quality option applies to JPEG and WebP; it has no effect on PNG.

await page.screenshot({
  path: 'compressed.jpg',
  type: 'jpeg',
  quality: 82
});

await page.screenshot({
  path: 'compressed.webp',
  type: 'webp',
  quality: 80
});

Use PNG for crisp text, transparency, or pixel-sensitive comparisons. Use JPEG or WebP when a smaller photographic or web-delivery asset matters more than lossless pixels.

CSS pixels versus device pixels

The scale option controls output density. scale: 'css' produces one output pixel per CSS pixel. scale: 'device' uses device pixels and is the Page API default, so high-DPI contexts can create a larger image.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.screenshot({
  path: 'css-scale.png',
  scale: 'css'
});

Choose one scale and keep it fixed for visual comparisons. Changing it changes the image dimensions even when the page itself has not changed.

Transparent output

omitBackground: true hides the default white background, which is useful for PNG overlays. It does not apply to JPEG, whose format cannot preserve transparency.

await page.screenshot({
  path: 'logo-overlay.png',
  omitBackground: true
});

Make captures repeatable on animated pages

Animations and asynchronous rendering are common reasons two captures differ. For a locator screenshot, animations: 'disabled' stops CSS and Web Animations while the image is taken.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
await page.locator('.hero').screenshot({
  path: 'hero-static.png',
  animations: 'disabled'
});

The locator API also supports a temporary style value for screenshot-specific CSS. Use it to hide a blinking cursor, freeze a transition, or remove a decoration that is irrelevant to the artifact, without changing the page permanently.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.locator('.dashboard').screenshot({
  path: 'dashboard.png',
  animations: 'disabled',
  style: '* { transition: none !important; animation: none !important; }'
});

Navigate before capturing and wait for the content your image actually needs. A successful goto only proves that navigation reached its chosen completion point; a client-rendered dashboard may still be filling in after that. Waiting for a meaningful locator is more reliable than adding an arbitrary long delay.

A production-friendly capture function

This version returns a Buffer, closes the browser even when navigation fails, and exposes the main choices to its caller.

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

async function capture(url, options = {}) {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage({
      viewport: options.viewport || { width: 1365, height: 768 }
    });

    await page.goto(url);

    return await page.screenshot({
      type: options.type || 'png',
      fullPage: Boolean(options.fullPage),
      scale: options.scale || 'css',
      omitBackground: Boolean(options.omitBackground)
    });
  } finally {
    await browser.close();
  }
}

(async () => {
  const image = await capture('https://example.com', { fullPage: true });
  require('node:fs').writeFileSync('example.png', image);
})();

Use an explicit output name when the format matters. In a service, also validate user-supplied URLs, set an execution timeout at the job boundary, and limit concurrent browsers so a queue cannot exhaust memory.

Keep ordinary captures separate from Playwright Test artifacts

The Page API is for an application, script, or one-off artifact. Playwright Test has separate screenshot workflows:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Automatic failure images: in the test configuration, use: { screenshot: 'only-on-failure' } captures screenshots only when a test fails. Other documented modes include off, on, and on-first-failure.
  • Visual assertions: await expect(page).toHaveScreenshot('page.png') belongs to the Playwright Test runner. It waits for two consecutive screenshots to stabilize before comparing them with the expected image.
  • Attachments: a Buffer can be attached to a test report with testInfo.attach('screenshot', { body: screenshot, contentType: 'image/png' }).

Do not add test-runner configuration merely to save a screenshot from a utility script. Conversely, do not replace a visual assertion with a manually saved image when the goal is regression detection.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common failures

Symptom Likely cause Fix
Cannot find module 'playwright' The dependency is not installed in the project from which Node is running. Install Playwright in that project and rerun the script from the same working directory.
Browser executable is missing The Playwright package is present but its browser binary has not been installed, or a custom executable path is invalid. Run the browser-install step required by your Playwright release, or remove the invalid custom path.
Navigation times out or never reaches the intended page The URL is unavailable, redirects indefinitely, requires authentication, or the chosen navigation completion condition is unsuitable. Check the URL from the same machine, handle authentication explicitly, and inspect the final page URL before taking the image.
Image is blank or shows a loading shell The screenshot ran before client-side content rendered. Wait for a locator that represents the finished content, then capture that page or locator.
Only the visible part of a long page appears Viewport capture is the default. Add fullPage: true.
Element screenshot fails because the target is not found The selector is wrong, the element is inside a frame, or it is created only after an interaction. Confirm the selector in the rendered DOM, target the correct frame, and perform the required interaction before locating it.
Element image is clipped The element is covered, not visible, or sits in a scrollable container. Make the element visible, remove overlays, and remember that a scrollable container captures its currently exposed content.
Transparent output is still opaque JPEG cannot carry an alpha channel. Use PNG with omitBackground: true.
Visual comparisons change between runs Animations, device-pixel scaling, fonts, time, or remote data changed. Fix the viewport and scale, disable animations for the capture, wait for stable content, and control other environment-dependent inputs.

Performance, reliability, and cost decisions

  • Reuse a browser when capturing many URLs: launching a fresh browser for every image adds startup work. Keep a controlled browser process and create isolated pages when your workload permits.
  • Prefer viewport images for previews: full-page images can become very tall and consume more memory and storage.
  • Use buffers for pipelines: skip temporary files when the next step uploads or transforms the image.
  • Make output deterministic: pin viewport dimensions, format, quality, scale, and animation behavior in the job definition.
  • Plan for failures: close pages and browsers in cleanup code, record the target URL and final URL, and retry only failures that are plausibly transient.
  • Local cost: Playwright itself does not charge per screenshot; your costs are the machine time, browser memory, storage, and any hosted browser infrastructure you choose.

Or skip the browser setup

If you only need a clean image from a URL, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response reports the result with X-Page-Verdict and X-Billed headers.

Use the one-call API documented at https://screenshotneo.com/docs/:

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

The same request from Python:

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)

And from 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}`);

ScreenshotNeo also supports full-page and CSS-selector captures, dark mode, device presets and custom viewports, retina scale, PDF output, custom CSS and JavaScript, pre-capture clicks, selector waits and delays, network-idle waits, ad and tracker blocking, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, an OpenAPI specification, and familiar parameter names for easier migration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account to try it without a card.

Frequently Asked Questions

Does fullPage change the browser viewport width?

No. It keeps the page width and extends the capture vertically across the scrollable document. Set the viewport explicitly when the rendered width matters.

Should I use a locator screenshot or a page screenshot for a component?

Use a locator when the artifact is one visible component; use a page screenshot when surrounding layout, overlays, and viewport context are part of what you need to inspect.

Can a Buffer be used without writing a PNG first?

Yes. Omitting path returns a Node.js Buffer that can be uploaded, attached to a test report, or transformed directly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.