ferrous-browser
Fast, async Rust browser automation via the Chrome DevTools Protocol — no Node.js required.
Why ferrous-browser?
Every Rust browser-automation library either wraps Node.js (slow, heavy) or is unmaintained. ferrous-browser is a pure-Rust, async-first CDP client with:
- Zero Node.js — pure Rust, ships as a single binary
- Async-first — built on Tokio; naturally integrates with any async Rust project
- Correct multi-page isolation — CDP session IDs are tracked; concurrent pages don't cross-contaminate events
- Race-condition-free — event handlers are registered before the commands that trigger them
- Ergonomic API — Playwright-inspired
locator(),evaluate(),WaitUntil
Installation
[]
= "0.1"
= { = "1", = ["full"] }
Requires Google Chrome or Chromium installed locally.
Quick start
use ;
async
Navigation wait modes
// Wait for DOM parsed (fast, sub-resources still loading)
page.goto.await?;
// Wait for all resources loaded (default)
page.goto.await?;
// Wait until no network activity for 500 ms (best for SPAs)
page.goto.await?;
Locator API
let page = browser.new_page.await?;
page.goto.await?;
// Click
page.locator.click.await?;
// Type
page.locator.type_text.await?;
// Wait until visible
page.locator.wait_for.await?;
// Read text / attribute
let text = page.locator.inner_text.await?;
let href = page.locator.get_attribute.await?;
Evaluate JavaScript
let count: u64 = page.evaluate.await?;
let is_logged_in: bool = page.evaluate.await?;
let title: String = page.evaluate.await?;
Browser configuration
use ;
use Duration;
let config = BrowserConfig ;
let browser = launch_chrome.await?;
| Field | Default | Description |
|---|---|---|
headless |
true |
Headless mode |
timeout |
30 s | Chrome startup deadline |
viewport |
1280×720 | Window size in logical pixels |
args |
[] |
Extra Chrome CLI flags |
Error handling
Every error carries structured context — no more "something went wrong":
use ;
match page.goto.await
.context() for chaining context onto any Result:
page.goto
.await
.context?;
Benchmarks
Micro-benchmarks measuring the library's internal message routing overhead are meaningless. Instead, we measure real-world wall-clock time for the most common end-to-end tasks.
Measured on macOS (Apple M-series), Chrome 147, using a warm browser instance (to eliminate launch overhead differences).
| Operation | ferrous-browser | Puppeteer | chromiumoxide |
|---|---|---|---|
New Page (about:blank) |
~466 ms | ~75 ms | ~100 ms |
Navigate + Content (example.com, load event) |
~735 ms | ~314 ms | ~277 ms |
| Screenshot (Full page PNG) | ~646 ms | ~138 ms | ~180 ms |
Performance Notes
- Target Attachment:
ferrous-browsernow usesTarget.setAutoAttachto automatically bind sessions for new pages, which cut page creation time by 40% (down from 750ms in earlier builds). However, Puppeteer's internal session routing is still more optimized for raw page creation throughput. - Screenshots:
ferrous-browsercaptures full-page screenshots by default (captureBeyondViewport: true), which requires Chrome to render the entire canvas. Puppeteer only captures the visible viewport by default, which is naturally faster.
Real-world examples
Web scraper
use ;
async
End-to-end test
use ;
async
Screenshot utility
use ;
async
Comparison
| ferrous-browser | chromiumoxide | headless_chrome | |
|---|---|---|---|
| Language | Rust | Rust | Rust |
| Node.js required | ❌ | ❌ | ❌ |
| Actively maintained | ✅ | ⚠️ stale | ❌ archived |
| Multi-page session isolation | ✅ | ✅ | ⚠️ |
page.evaluate::<T>() |
✅ | ✅ | ✅ |
| Locator API | ✅ | ❌ | ❌ |
WaitUntil::NetworkIdle |
✅ | ❌ | ❌ |
| Structured errors | ✅ | ⚠️ | ⚠️ |
Roadmap
-
page.set_cookies()/page.cookies()— session persistence -
page.pdf()— PDF export -
page.evaluate_handle()— remote object references - Structured trace/HAR capture
- CI matrix: Linux + macOS + Windows / stable + beta Chrome
- Cross-platform: replace
nixfor Windows support
License
Dual licensed under MIT OR Apache-2.0 at your option.