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

Use Selenium’s JavaScript binding, call await driver.takeScreenshot(), and write the returned Base64 string with Node’s base64 encoding. For one element, locate it and call await element.takeScreenshot(true). The examples below install the current package, save valid PNG files, explain capture scope and remote-browser behavior, and show how to avoid common failures.

What Selenium returns

Selenium’s WebDriver API defines takeScreenshot() as a screenshot of the current page. In JavaScript, the method resolves to a Base64-encoded PNG string, not a data-URL and not UTF-8 text. The documented best-effort order is:

  1. the entire page, when the browser can provide it;
  2. the current browser window;
  3. the visible portion of the current frame; and
  4. the entire display containing the browser.

Because the result is Base64, pass 'base64' to Node’s file-writing function. Omitting that argument creates a corrupt image or writes the encoded characters instead of PNG bytes.

Prerequisites and installation

  • Node.js 22 or newer, as required by the current official Selenium JavaScript API page.
  • A supported browser such as Chrome and a matching WebDriver setup. Selenium Manager can obtain drivers for many local configurations; a remote Selenium server can also be used.
  • A project directory with permission to create the output file.
mkdir selenium-shots
cd selenium-shots
npm init -y
npm install selenium-webdriver

The npm package version and download counts change over time, so pin a version in production after reviewing the version your project has validated.

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

Capture and save a page screenshot

This complete script opens a page, captures the current browsing context, writes screenshot.png, and always closes the browser.

const { Builder, Browser } = require('selenium-webdriver');
const fs = require('node:fs');

(async function saveScreenshot() {
  const driver = await new Builder()
    .forBrowser(Browser.CHROME)
    .build();

  try {
    await driver.get('https://example.com');
    const encoded = await driver.takeScreenshot();
    fs.writeFileSync('./screenshot.png', encoded, 'base64');
    console.log('Saved ./screenshot.png');
  } finally {
    await driver.quit();
  }
})();

Save it as page-shot.js and run node page-shot.js. The file is a regular PNG that image viewers and CI artifacts can open.

Wait for the page you actually want to capture

driver.get() waits for the navigation to complete, but applications often render important content afterward. Wait for a specific element before taking the shot.

const { Builder, Browser, By, until } = require('selenium-webdriver');
const fs = require('node:fs');

(async function captureReadyPage() {
  const driver = await new Builder().forBrowser(Browser.CHROME).build();
  try {
    await driver.get('https://example.com/dashboard');
    const chart = await driver.wait(
      until.elementLocated(By.css('[data-testid="sales-chart"]')),
      15000
    );
    await driver.wait(until.elementIsVisible(chart), 10000);
    const encoded = await driver.takeScreenshot();
    fs.writeFileSync('./dashboard.png', encoded, 'base64');
  } finally {
    await driver.quit();
  }
})();

Use an application-specific readiness signal rather than an arbitrary sleep whenever possible. If a chart animates, wait for its completed state or add a short, measured delay after it becomes visible.

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

Capture one element

Locate the element and call takeScreenshot(true). The boolean requests a scroll-into-view operation before capture, which is useful for content below the fold.

const { Builder, Browser, By } = require('selenium-webdriver');
const fs = require('node:fs');

(async function saveElementScreenshot() {
  const driver = await new Builder().forBrowser(Browser.CHROME).build();
  try {
    await driver.get('https://example.com');
    const heading = await driver.findElement(By.css('h1'));
    const encoded = await heading.takeScreenshot(true);
    fs.writeFileSync('./heading.png', encoded, 'base64');
  } finally {
    await driver.quit();
  }
})();

Element capture follows the element’s rendered box, including only what the browser can capture for that element. A selector that matches nothing raises a no-such-element error; a hidden or detached element can produce a stale-element or visibility failure.

Control the capture context

Frames

Selenium captures the current frame context. To capture content inside an iframe, switch into it first, capture, then switch back if later steps use the top document.

const frame = await driver.findElement(By.css('iframe.payment'));
await driver.switchTo().frame(frame);
const encoded = await driver.takeScreenshot();
fs.writeFileSync('./frame.png', encoded, 'base64');
await driver.switchTo().defaultContent();

Windows and tabs

A screenshot applies to the active window handle. After opening a tab, switch to its handle before calling the method; otherwise you will capture whichever tab is currently selected.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const handles = await driver.getAllWindowHandles();
await driver.switchTo().window(handles[handles.length - 1]);
const encoded = await driver.takeScreenshot();
fs.writeFileSync('./new-tab.png', encoded, 'base64');

Viewport, responsive layout, and pixel density

Set the window size before navigation when visual consistency matters. The exact pixel dimensions and device scale are controlled by the browser and WebDriver environment, so screenshots from local and remote machines can differ.

await driver.manage().window().setRect({ width: 1440, height: 900 });
await driver.get('https://example.com');

Selenium’s screenshot API does not provide a universal “full-page” guarantee across every browser. It asks the driver for the best available result in the order listed earlier. If your test requires a precisely defined long-page image, verify the result on the browser/driver combination used by your pipeline.

Run the same code against a remote Selenium server

Replace the local builder with a remote URL and capabilities appropriate to your Grid or cloud provider. The screenshot still resolves to Base64, so the save code is unchanged.

const { Builder, Browser } = require('selenium-webdriver');
const fs = require('node:fs');

(async function remoteShot() {
  const driver = await new Builder()
    .forBrowser(Browser.CHROME)
    .usingServer('http://localhost:4444/wd/hub')
    .build();
  try {
    await driver.get('https://example.com');
    const encoded = await driver.takeScreenshot();
    fs.writeFileSync('./remote.png', encoded, 'base64');
  } finally {
    await driver.quit();
  }
})();

With a remote session, the browser runs on the server while the PNG data travels back to your Node process. Network latency and session timeouts therefore affect screenshot duration and reliability.

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

Reusable screenshot helper

Centralizing file writing makes test code less error-prone and lets you create unique names for parallel runs.

const fs = require('node:fs/promises');

async function savePng(base64, path) {
  if (typeof base64 !== 'string' || base64.length === 0) {
    throw new Error('WebDriver returned empty screenshot data');
  }
  await fs.writeFile(path, base64, 'base64');
}

// Example:
// await savePng(await driver.takeScreenshot(), `artifacts/${name}.png`);

Create the artifact directory before writing, sanitize names derived from URLs or test titles, and avoid two workers writing the same path.

Performance, reliability, and cost considerations

  • Capture only what you need. An element shot transfers less data than a very tall page and is usually faster.
  • Do not screenshot every polling attempt. Capture on failure, at checkpoints, or after a deterministic readiness condition.
  • Keep sessions short. Reuse a driver for related steps, but always quit it in a finally block so failed tests do not leak browser processes.
  • Stabilize rendering. Fix the viewport, fonts, locale, timezone, animation state, and test data when comparing images.
  • Budget remote transfer. Remote screenshots consume time and bandwidth in addition to browser execution; retain only the artifacts needed for debugging or review.
  • Selenium itself has no screenshot fee. Your costs come from the machine or hosted WebDriver service, storage, and CI runtime.

Troubleshooting common failures

“Cannot find module selenium-webdriver”

Install the dependency in the directory from which the script runs: npm install selenium-webdriver. Confirm that node_modules and package.json are in the current project.

Node version or syntax errors

Use Node.js 22 or newer for the current official JavaScript binding requirements. Check with node --version, then upgrade the runtime used by both your shell and CI job.

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

Driver or browser session will not start

Verify that Chrome is installed and reachable, that the remote endpoint is correct, and that the server supports the requested browser. In containers, check executable paths, sandbox permissions, and virtual-display configuration.

The PNG is unreadable or contains Base64 text

Write the value with fs.writeFileSync(path, encoded, 'base64') (or the equivalent promise API). Do not prepend a data-URL header and do not use UTF-8 encoding.

The page is blank or incomplete

Wait for a meaningful selector, switch into the correct frame or window, and confirm that authentication and network requests finished. For lazy content, scroll or trigger the application’s own loading condition before capture.

Element screenshot throws an exception

Check that the selector is unique, the element is displayed, and it has not been replaced by a framework render. Locate it again immediately before capture when the DOM is highly dynamic.

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

Different pixels in CI

Use the same browser version, viewport, fonts, locale, timezone, and device configuration. Disable or wait for animations, and compare with a tolerance rather than requiring byte-for-byte identity across different graphics stacks.

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

Or skip the browser setup

ScreenshotNeo returns a website screenshot or PDF from one GET request. Before the capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and whether it was billed. It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

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

JavaScript:

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

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)

See the ScreenshotNeo documentation for output and option details. Every plan includes the features above, including full-page lazy-image loading, element selectors, dark mode, device presets, custom CSS and JavaScript, click and wait actions, request blocking, headers and cookies, geolocation and timezone, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Choosing the right approach

Need Best fit Reason
Assertions while a test is running Selenium WebDriver The browser is already driving the page, so capture the current state directly.
One element for a test artifact element.takeScreenshot(true) Produces a focused PNG after scrolling the element into view.
Standalone URLs, PDFs, or automated cleanup ScreenshotNeo One request handles capture and removes common consent UI before billing only clean results.
AI-agent initiated captures ScreenshotNeo MCP server Tools are available to MCP clients such as Claude and Cursor.

FAQ

Can Selenium save JPEG or WebP?

The JavaScript WebDriver screenshot method documented here returns a Base64-encoded PNG. Convert the PNG afterward if another image format is required.

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

Does takeScreenshot() capture browser chrome?

No. It captures the page or browser display area according to the WebDriver implementation, not operating-system menus or other applications.

Can I capture a screenshot before quitting the driver?

Yes. Capture and write the file while the session is active, then call quit() in cleanup.

Frequently Asked Questions

Can Selenium save JPEG or WebP?

The JavaScript WebDriver screenshot method returns a Base64-encoded PNG. Convert the PNG afterward if another image format is required.

Does takeScreenshot() capture browser chrome?

No. It captures the page or browser display area according to the WebDriver implementation, not operating-system menus or other applications.

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.

Can I capture a screenshot before quitting the driver?

Yes. Capture and write the file while the session is active, then call quit() in cleanup.

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.