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 ferrumYou 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.quitFull-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 elementPros 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-webdriverChromeDriver 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
endFull-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.closePros 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 snapsharpOr 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
endSidekiq 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
| Feature | Ferrum | Selenium | SnapSharp Gem |
|---|---|---|---|
| Dependencies | Chrome binary | Chrome + ChromeDriver | HTTP client only |
| Install | gem install ferrum | gem install selenium-webdriver | gem install snapsharp |
| Full-page | Built-in | CDP workaround | API parameter |
| Dark mode | JS injection | Chrome flags | dark_mode: true |
| Element capture | Built-in | Manual crop | selector param |
| Built-in | Limited | API parameter | |
| Ad blocking | Network interception | Extension loading | block_ads: true |
| RAM usage | ~200 MB | ~250 MB | Minimal |
| Heroku-friendly | Needs buildpack | Needs buildpack | Works out of the box |
| Best for | Local automation, scraping | Cross-browser testing | Production 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.