How to Take Website Screenshots with Selenium in JavaScript
Capture a website as a PNG with Selenium WebDriver in JavaScript. Learn setup, full-page caveats, element captures, troubleshooting, and an API alternative.
Selenium WebDriver can capture a website screenshot in JavaScript with await driver.takeScreenshot(). It returns a base64-encoded PNG; write that string to disk using the base64 encoding. The example below opens Chrome, navigates to a URL, saves screenshot.png, and always closes the browser.
1. Install Selenium and prepare a browser
Use a Node.js version supported by the current Selenium JavaScript binding. Selenium’s current API documentation says Node.js 22 or newer is required; check the official API page for current runtime support. Install the package in your project:
npm install selenium-webdriver
The example uses Chrome. Make Chrome available in the environment; Selenium Manager can handle browser-driver setup for supported configurations. A headless browser is commonly used on servers and CI systems, but it still needs a functioning browser installation and compatible runtime environment.
2. Capture and save a website screenshot
Save this as screenshot.js and run it with node screenshot.js:
const { Builder, Browser } = require('selenium-webdriver')
const fs = require('node:fs')
async function capture() {
const driver = await new Builder().forBrowser(Browser.CHROME).build()
try {
await driver.get('https://example.com')
const encodedPng = await driver.takeScreenshot()
fs.writeFileSync('./screenshot.png', encodedPng, 'base64')
console.log('Saved ./screenshot.png')
} finally {
await driver.quit()
}
}
capture().catch((error) => {
console.error(error)
process.exitCode = 1
})
The finally block matters: it closes the browser even if navigation or capture fails. takeScreenshot() returns a promise, so await it before writing the file. The output is PNG; do not prepend a data URL prefix such as data:image/png;base64, when saving it this way.
3. Understand screenshot scope
Selenium documents screenshot capture as best effort. Its JavaScript WebDriver API gives this preference order: the entire page, the current window, the visible portion of the current frame, then the display containing the browser. This is not a guarantee that every browser and driver combination will return a full-page image. Inspect the resulting image dimensions and content in the environment where the script will run.
For a screenshot of one element, locate it and call the element’s screenshot method. The capture is the visible region within that element’s bounding rectangle:
const element = await driver.findElement({ css: 'main article' })
const encodedPng = await element.takeScreenshot()
fs.writeFileSync('./article.png', encodedPng, 'base64')
This captures the element’s visible region; it does not mean that a long, scrollable element will always be stitched into a complete image. If the target is not visible or is covered, scroll it into view or wait for the page state you need before capture.
4. Wait for the page to be ready
A successful navigation does not ensure that client-rendered content, fonts, images, or animations have finished. Wait for a page-specific condition before taking the screenshot. For example, wait until a known element is present:
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')
await driver.wait(until.elementLocated(By.css('main')), 10000)
const encodedPng = await driver.takeScreenshot()
fs.writeFileSync('./ready.png', encodedPng, 'base64')
} finally {
await driver.quit()
}
}
captureReadyPage().catch(console.error)
Choose a selector that indicates the content is actually ready, not merely that the document exists. For dynamic pages, you may also wait for a loading indicator to disappear or for a specific application state. Avoid relying on a fixed long sleep unless the page offers no observable readiness condition; fixed delays waste time on fast loads and can still be too short on slow ones.
5. Run capture through a remote Selenium server
When the browser runs on a separate Selenium server, configure the Builder with that server URL. The server address and browser availability depend on your own environment:
const { Builder, Browser } = require('selenium-webdriver')
const fs = require('node:fs')
async function captureRemote() {
const driver = await new Builder()
.usingServer('http://localhost:4444')
.forBrowser(Browser.CHROME)
.build()
try {
await driver.get('https://example.com')
const encodedPng = await driver.takeScreenshot()
fs.writeFileSync('./remote.png', encodedPng, 'base64')
} finally {
await driver.quit()
}
}
captureRemote().catch(console.error)
Selenium also documents configuring the remote server through SELENIUM_REMOTE_URL. A remote session moves browser execution to the configured server; it does not change the screenshot method’s base64 PNG result.
6. Choose PNG or PDF
takeScreenshot() produces a PNG image. If the required artifact is a PDF representation of the current page, Selenium’s JavaScript API provides printPage(options), also documented as best effort:
const pdfBase64 = await driver.printPage()
fs.writeFileSync('./page.pdf', pdfBase64, 'base64')
PDF printing is a different output path from a full-page PNG screenshot. Use it when the deliverable is a printable PDF, and consult the WebDriver API for the supported print options.
7. Save a screenshot with cURL, Python, or Node.js using ScreenshotNeo
If you need an image from a URL without starting a browser session, ScreenshotNeo provides a screenshot API. Its endpoint returns an image or PDF; this request saves the response as WebP. See the ScreenshotNeo API documentation for options 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()
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}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
8. Or skip the browser setup
With Selenium, you manage a browser session and its readiness. ScreenshotNeo takes a URL in one API request. Cookie banners, newsletter popups, and chat widgets are removed before the shot, and each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and billing status. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.
Create a free account and get 1,000 screenshots a month with no card.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Cannot find module 'selenium-webdriver' |
The package is not installed in this project, or the script is running from a different project directory. | Run npm install selenium-webdriver in the project and execute the script there. |
| Node version or syntax/runtime errors during setup | The installed Node.js version is outside the binding’s supported range. | Check the current Selenium JavaScript API requirements and use a supported Node.js release. |
| Browser or driver cannot be found | The browser is missing, unavailable to the process, or Selenium Manager cannot resolve the environment. | Install or expose the selected browser, check execution permissions and environment paths, and review Selenium Manager setup guidance. |
| Connection refused for a remote session | The configured Selenium server is not running or the URL/port is wrong. | Start the server, verify its reachable URL from the script’s host, and check the Builder or SELENIUM_REMOTE_URL configuration. |
| Screenshot is blank or missing page content | The page may still be rendering, require interaction, or have failed navigation. | Wait for a meaningful page selector, check navigation errors, and capture only after required content is visible. |
| Image shows only part of a long page | Full-page output is best effort and varies with the browser and driver context. | Inspect the output dimensions, verify the environment’s behavior, or use a capture method designed for the required page scope. |
| Saved file is corrupt or unreadable | The returned base64 string was not written as base64, or a data URL prefix was included. | Write the raw return value using fs.writeFileSync(path, encodedPng, 'base64'). |
| Browser remains open after a failure | The script did not quit the driver on every path. | Put await driver.quit() in a finally block. |
10. Performance, reliability, and cost
- Performance: Browser startup and page loading generally dominate the capture flow. Reuse a session when capturing multiple pages in one job, while still closing it in a
finallyblock. Wait for the actual content you need rather than adding a large fixed delay. - Reliability: Screenshot capture is best effort. Page state, browser/driver setup, network conditions, and remote session availability all affect the result. For automated jobs, record the target URL and error, set sensible timeouts, and ensure sessions are closed when a capture fails.
- Cost: The Selenium API documentation does not specify a price for running a browser. Your execution cost depends on where and how you run the browser or remote Selenium server. ScreenshotNeo has a free tier of 1,000 shots monthly and paid plans from $5 for 3,000; only clean shots are billed.
11. FAQ
Does Selenium return a PNG or a base64 string?
The method returns a base64-encoded PNG string. Decode it as base64 when writing the file.
Can I screenshot one element?
Yes. Find the element and call element.takeScreenshot() to capture its visible bounding rectangle.
Does takeScreenshot() always capture the entire page?
No. Selenium documents a best-effort preference for the whole page, followed by narrower browser contexts. Confirm the output in your target environment.
How do I get a PDF instead of an image?
Use driver.printPage() for a PDF representation of the current page; it is separate from screenshot capture.
Sources
- Selenium WebDriver JavaScript API — package, Node.js requirement, setup, and remote server configuration.
- WebDriver class API — screenshot behavior and PDF printing.
- Working with windows and tabs — JavaScript screenshot and base64 file-writing example.
- WebElement class API — element screenshot scope.


