ScreenshotNeo

BlogHow-to

How to Capture Screenshots with Selenium in Ruby

Capture viewport, full-page, element, PNG, and base64 screenshots in Selenium Ruby with runnable code, waits, troubleshooting, and production guidance.

By the ScreenshotNeo team1 October 20267 min read

Use Selenium WebDriver’s save_screenshot method after the page reaches the state you want to capture. It writes the current browser viewport as a PNG file. For image data in memory, use screenshot_as(:png) or screenshot_as(:base64). Full-page capture is available only when the active browser driver supports it.

This guide shows complete Ruby examples for viewport, full-page, element, file, byte, and Base64 screenshots, plus waits, browser setup, troubleshooting, and production considerations.

1. Install Selenium and start a browser

Add the Selenium Ruby gem:

gem install selenium-webdriver

Recent Selenium versions can manage compatible browser drivers through Selenium Manager. You still need a browser such as Chrome or Firefox installed. In CI, install the browser and run it headlessly when no display is available.

require "selenium-webdriver"

options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

driver = Selenium::WebDriver.for(:chrome, options: options)
begin
  driver.navigate.to("https://example.test")
  driver.save_screenshot("tmp/example.png")
ensure
  driver.quit
end

save_screenshot saves a PNG of the viewport to the path you provide. Make sure the directory exists and is writable, and use a filename ending in .png. The Selenium Ruby API documents this as saving a PNG screenshot of the viewport. See the Selenium Ruby TakesScreenshot API.

2. Capture a reliable screenshot after the page is ready

A screenshot records the browser state at the instant the method runs. Navigation completing does not guarantee that fonts, images, asynchronous data, or a modal are ready. Wait for a meaningful condition before capturing.

Wait for an element

wait = Selenium::WebDriver::Wait.new(timeout: 15)
hero = wait.until do
  element = driver.find_element(css: "main .hero")
  element if element.displayed?
end

driver.save_screenshot("tmp/ready.png")

Wait for a specific text or state

wait.until do
  driver.find_element(css: "[data-status='loaded']").attribute("data-ready") == "true"
end

driver.save_screenshot("tmp/loaded.png")

Wait for a fixed delay only when necessary

sleep 1.5
driver.save_screenshot("tmp/after-delay.png")

Prefer an explicit condition over a fixed sleep. A sleep can be too short on a slow run and unnecessarily slow on a fast run.

3. Save a viewport screenshot as PNG

The basic call captures the visible viewport, including the current scroll position.

driver.save_screenshot("tmp/viewport.png")

Use a deterministic directory in scripts and tests:

require "fileutils"
FileUtils.mkdir_p("tmp/screenshots")
driver.save_screenshot("tmp/screenshots/home.png")

4. Capture a full-page screenshot

Pass full_page: true when the active driver supports full-page screenshots:

driver.save_screenshot("tmp/full-page.png", full_page: true)

Full-page support is driver-dependent, not universal. Selenium checks whether the driver implements the full-page operation and raises Selenium::WebDriver::Error::UnsupportedOperationError when it does not.

begin
  driver.save_screenshot("tmp/full-page.png", full_page: true)
rescue Selenium::WebDriver::Error::UnsupportedOperationError
  warn "This browser driver does not support full-page screenshots"
  driver.save_screenshot("tmp/viewport-fallback.png")
end

If full-page capture is unavailable, alternatives include taking several viewport shots while scrolling and stitching them, using a browser or driver with full-page support, or using a screenshot API.

5. Get screenshot bytes or Base64 without writing a file

Use screenshot_as when you need to upload the image, attach it to a test report, embed it in HTML, or pass it to another service.

png_bytes = driver.screenshot_as(:png)
File.binwrite("tmp/from-bytes.png", png_bytes)

base64_data = driver.screenshot_as(:base64)
File.write("tmp/screenshot.txt", base64_data)

The documented formats are :png and :base64. An unsupported format raises UnsupportedOperationError. Base64 output is the encoded PNG payload; when embedding it in HTML, prepend data:image/png;base64,.

html = "<img src=\"data:image/png;base64,#{base64_data}\" alt=\"Screenshot\">"
File.write("tmp/report.html", html)

6. Capture one element

Selenium’s screenshot interface is also available on element objects when the binding and browser driver support element capture.

card = driver.find_element(css: ".pricing-card")
card.save_screenshot("tmp/pricing-card.png")

Element screenshots are useful for component tests and documentation. Verify support in the browser and driver you run in CI; behavior can vary by driver.

7. Complete Ruby example

require "selenium-webdriver"
require "fileutils"

FileUtils.mkdir_p("tmp")

options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

driver = Selenium::WebDriver.for(:chrome, options: options)
wait = Selenium::WebDriver::Wait.new(timeout: 20)

begin
  driver.navigate.to("https://example.test")

  wait.until do
    element = driver.find_element(css: "main")
    element.displayed?
  end

  driver.save_screenshot("tmp/viewport.png")

  begin
    driver.save_screenshot("tmp/full-page.png", full_page: true)
  rescue Selenium::WebDriver::Error::UnsupportedOperationError
    warn "Full-page screenshots are not supported by this driver"
  end

  png_bytes = driver.screenshot_as(:png)
  File.binwrite("tmp/viewport-from-bytes.png", png_bytes)

  base64_data = driver.screenshot_as(:base64)
  File.write("tmp/viewport.base64", base64_data)
ensure
  driver.quit
end

8. Browser and capture options that affect the result

Need Ruby/Selenium approach Notes
Desktop dimensions --window-size=1440,1000 Set before navigation for responsive layouts.
Mobile layout Use a mobile emulation profile or a mobile-capable driver Confirm the target driver supports the emulation settings.
Headless CI --headless=new Use a virtual display or headed mode when diagnosing rendering differences.
Retina-like pixels Set the browser’s device scale factor where supported Output dimensions and page CSS pixels can differ.
Authenticated pages Log in through Selenium or set cookies before capture Do not put credentials in URLs or committed code.
Stable animation Disable animations with injected CSS or wait for a finished state Animations can produce different frames between runs.

9. Troubleshooting common errors

“Unable to find a driver” or browser startup failure

Cause: The browser is missing, the driver is unavailable, or the CI environment cannot launch a display.

Fix: Install the browser, update Selenium, use Selenium Manager or configure a matching driver, and add headless options in display-less environments.

The PNG file is missing or empty

Cause: The destination directory does not exist, the process lacks write permission, or the process exits before the call completes.

Fix: Create the directory, use an absolute or known writable path, and keep driver.quit in ensure after the screenshot call.

UnsupportedOperationError with full_page: true

Cause: The active driver does not implement full-page capture.

Fix: Catch the exception and fall back to a viewport image, use a supported driver, or stitch scrolled captures.

The screenshot shows a loading spinner or blank component

Cause: Capture happened before asynchronous rendering finished.

Fix: Wait for a selector, visibility, text, network-driven state exposed by the application, or a bounded delay when no observable condition exists.

Fonts or images differ from a local run

Cause: The CI machine may lack fonts, use a different browser version, or capture before web fonts and images load.

Fix: Install required fonts, pin browser versions where practical, wait for the relevant elements, and compare viewport and device scale settings.

The element screenshot is unsupported

Cause: Element-level capture depends on the binding and driver.

Fix: Capture the viewport after scrolling the element into view, or run with a driver that supports element screenshots.

10. Performance, reliability, and cost considerations

  • Reuse one driver for a related batch of pages when isolation permits; browser startup is usually more expensive than an individual screenshot.
  • Quit every driver in an ensure block so failed tests do not leak browser processes.
  • Use explicit waits with reasonable timeouts. Very long timeouts hide real failures; very short ones create flaky captures.
  • Full-page images consume more memory and disk space than viewport images. Keep dimensions and retention appropriate for your artifact store.
  • For parallel jobs, give each worker its own driver and output filename.
  • Local Selenium has no per-shot API charge, but you operate the browser, driver, machines, storage, and maintenance yourself.

11. Or skip the browser setup

If you need a clean screenshot from a URL without managing WebDriver, ScreenshotNeo provides a GET endpoint for PNG, JPEG, WebP, or PDF output. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing status.

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

ScreenshotNeo also supports full-page capture, CSS element selection, dark mode, device presets, retina scale, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, geolocation, resizing, chosen cache TTLs, signed links, asynchronous jobs, webhooks, bulk capture, usage reporting, and PDF output. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Free accounts include 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.

12. FAQ

Does Selenium save JPEG or WebP screenshots?

The Ruby TakesScreenshot API documents PNG and Base64 output. Convert the PNG afterward if another format is required.

Does save_screenshot capture the whole page by default?

No. It captures the viewport by default. Request full_page: true only when the driver supports it.

Can I take a screenshot before calling driver.quit?

Yes. Capture while the driver is active, then quit it in an ensure block.

What is the best output for an API upload?

Use screenshot_as(:png) for binary upload, or screenshot_as(:base64) when the receiving interface expects text.