Menu
rubytutorialscreenshots

How to Take Website Screenshots in Ruby — Complete Guide

SnapSharp Team·March 24, 2026·7 min read

How to Take Website Screenshots in Ruby

Ruby developers have several options for capturing website screenshots — from local browser automation to cloud APIs. This guide covers three approaches with production-ready code.

Method 1: Ferrum

Ferrum is a modern, high-level Ruby library for controlling Chrome via the DevTools Protocol. It's fast, lightweight, and doesn't require Selenium.

Installation

gem install ferrum

You need Chrome or Chromium installed on your system.

Basic Screenshot

require "ferrum"

browser = Ferrum::Browser.new(
  headless: true,
  window_size: [1280, 720]
)

browser.goto("https://example.com")
browser.screenshot(path: "screenshot.png")
browser.quit

Full-Page Screenshot

browser.goto("https://example.com")
browser.screenshot(path: "full.png", full: true)

Dark Mode via CSS Override

Ferrum doesn't have a built-in dark mode toggle, but you can inject CSS media emulation:

browser.goto("https://example.com")

browser.execute(<<~JS)
  window.matchMedia = function(query) {
    return {
      matches: query === '(prefers-color-scheme: dark)',
      media: query,
      addEventListener: function() {},
      removeEventListener: function() {},
    };
  };
JS

browser.screenshot(path: "dark.png")

PDF Export

browser.goto("https://example.com")
browser.pdf(path: "page.pdf", landscape: false, format: "A4")

Wait for Dynamic Content

browser.goto("https://example.com")
browser.network.wait_for_idle(timeout: 10)
browser.screenshot(path: "loaded.png")

Capture Specific Element

browser.goto("https://example.com")
element = browser.at_css(".hero-section")
element.screenshot(path: "hero.png") if element

Pros and Cons

Pros: Pure Ruby (no Java/Selenium), fast CDP communication, supports PDF, element screenshots, and network interception.

Cons: Requires Chrome installed locally, ~200 MB RAM per browser instance, you manage process lifecycle, limited community compared to Selenium.


Method 2: Selenium WebDriver

Selenium WebDriver is the venerable browser automation standard. Ruby has official bindings.

Installation

gem install selenium-webdriver

ChromeDriver must match your Chrome version. The selenium-webdriver gem (v4+) manages driver downloads automatically.

Basic Screenshot

require "selenium-webdriver"

options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,720")
options.add_argument("--no-sandbox")
options.add_argument("--disable-gpu")

driver = Selenium::WebDriver.for(:chrome, options: options)

begin
  driver.get("https://example.com")
  driver.save_screenshot("screenshot.png")
ensure
  driver.quit
end

Full-Page Screenshot

Selenium's Chrome driver supports full-page screenshots via CDP:

result = driver.execute_cdp("Page.captureScreenshot", {
  captureBeyondViewport: true
})

require "base64"
File.binwrite("full.png", Base64.decode64(result["data"]))

Set Cookies (Authenticated Screenshots)

driver.get("https://app.example.com")
driver.manage.add_cookie(
  name: "session",
  value: "your_session_token",
  domain: "app.example.com"
)
driver.get("https://app.example.com/dashboard")
driver.save_screenshot("dashboard.png")

Reusable Helper Class

class ScreenshotService
  def initialize
    options = Selenium::WebDriver::Chrome::Options.new
    options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    @driver = Selenium::WebDriver.for(:chrome, options: options)
  end

  def capture(url, output, width: 1280, height: 720)
    @driver.manage.window.resize_to(width, height)
    @driver.get(url)
    @driver.save_screenshot(output)
  end

  def close
    @driver.quit
  end
end

service = ScreenshotService.new
service.capture("https://example.com", "example.png")
service.capture("https://github.com", "github.png")
service.close

Pros and Cons

Pros: Mature ecosystem, official Ruby bindings, cross-browser support, large community.

Cons: Slower than Ferrum, verbose API, ChromeDriver version management, full-page screenshots require CDP hacks.


Method 3: SnapSharp Gem

The SnapSharp gem provides a clean Ruby interface to the SnapSharp cloud API. No local browser required.

Installation

gem install snapsharp

Or in your Gemfile:

gem "snapsharp"

Basic Screenshot

require "snapsharp"

client = SnapSharp::Client.new(api_key: ENV["SNAPSHARP_API_KEY"])

image = client.screenshot("https://example.com", width: 1280, height: 720)
File.binwrite("screenshot.png", image)

Full-Page + Dark Mode

image = client.screenshot("https://example.com",
  full_page: true,
  dark_mode: true,
  format: "webp",
  block_ads: true
)
File.binwrite("dark-full.webp", image)

Element Capture

image = client.screenshot("https://example.com",
  selector: ".pricing-table",
  width: 1440
)

PDF Export

pdf = client.screenshot("https://example.com",
  format: "pdf",
  pdf_format: "A4"
)
File.binwrite("page.pdf", pdf)

Raw HTTP (without gem)

If you prefer not to add the gem, use net/http:

require "net/http"
require "uri"

def screenshot(url, output, **options)
  params = { url: url, width: 1280, height: 720 }.merge(options)
  uri = URI("https://api.snapsharp.dev/v1/screenshot")
  uri.query = URI.encode_www_form(params)

  request = Net::HTTP::Get.new(uri)
  request["Authorization"] = "Bearer #{ENV['SNAPSHARP_API_KEY']}"

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end

  raise "Screenshot failed: #{response.code}" unless response.code == "200"
  File.binwrite(output, response.body)
end

screenshot("https://example.com", "example.png")

Rails Integration

# app/services/screenshot_service.rb
class ScreenshotService
  def initialize
    @client = SnapSharp::Client.new(api_key: Rails.application.credentials.snapsharp_api_key)
  end

  def capture(url, width: 1280, height: 720, **options)
    @client.screenshot(url, width: width, height: height, **options)
  end

  def capture_and_store(url, filename)
    image = capture(url)
    blob = ActiveStorage::Blob.create_and_upload!(
      io: StringIO.new(image),
      filename: filename,
      content_type: "image/png"
    )
    blob.url
  end
end

Sidekiq Background Job

class ScreenshotJob
  include Sidekiq::Job

  def perform(url, storage_key)
    service = ScreenshotService.new
    image = service.capture(url, full_page: true)

    S3_BUCKET.put_object(
      key: storage_key,
      body: image,
      content_type: "image/png"
    )
  end
end

# Enqueue:
ScreenshotJob.perform_async("https://example.com", "screenshots/example.png")

Pros and Cons

Pros: No local browser, works on Heroku/Railway/Render without buildpacks, typed responses, automatic retries, Rails-friendly.

Cons: Network latency (~2–4s), API costs at scale (100 free/month), requires internet access.


Comparison Table

FeatureFerrumSeleniumSnapSharp Gem
DependenciesChrome binaryChrome + ChromeDriverHTTP client only
Installgem install ferrumgem install selenium-webdrivergem install snapsharp
Full-pageBuilt-inCDP workaroundAPI parameter
Dark modeJS injectionChrome flagsdark_mode: true
Element captureBuilt-inManual cropselector param
PDFBuilt-inLimitedAPI parameter
Ad blockingNetwork interceptionExtension loadingblock_ads: true
RAM usage~200 MB~250 MBMinimal
Heroku-friendlyNeeds buildpackNeeds buildpackWorks out of the box
Best forLocal automation, scrapingCross-browser testingProduction APIs, SaaS

Which Should You Choose?

  • Ferrum if you want a modern, fast Ruby library for local browser automation with a clean API.
  • Selenium if you need cross-browser support or have an existing Selenium test suite.
  • SnapSharp gem if you want zero-ops screenshots that work on any hosting platform without Chrome buildpacks.

For Heroku and similar PaaS where installing Chrome is painful, the API approach is by far the easiest path.

Get a free SnapSharp API key →