Skip to content

Repository files navigation

Playwright for Rust

crates.io docs.rs CI License Playwright skills.sh

Rust language bindings for Microsoft Playwright — the industry standard for cross-browser end-to-end testing.

Status: Pre-1.0, API stabilizing. See coverage for the path to v1.0.

This README describes the latest published release on crates.io. For unreleased changes on main, see the CHANGELOG under [Unreleased].

Why playwright-rust?

Rust is emerging as a serious web development language, AI coding assistants are making it accessible to more developers, and test-driven development works well with AI agents. Those trends need production-quality E2E testing, and playwright-rust fills that gap (WHY.md has the fuller case). The goal is official-quality Rust bindings following the same architecture as playwright-python, playwright-java, and playwright-dotnet; see the development roadmap for plans and status.

Quick comparison: Python vs Rust

If you know playwright-python, you know playwright-rust:

PythonRust
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://example.com")

    # Locator with auto-waiting
    heading = page.locator("h1")
    assert heading.text_content() == "Example Domain"

    # Response body access
    resp = page.goto("https://api.example.com/data")
    data = resp.json()

    browser.close()
use playwright_rs::Playwright;

let pw = Playwright::launch().await?;
let browser = pw.chromium().launch().await?;
let page = browser.new_page().await?;
page.goto("https://example.com", None).await?;

// Locator with auto-waiting
let heading = page.locator("h1").await;
assert_eq!(heading.text_content().await?, Some("Example Domain".into()));

// Response body access
let resp = page.goto("https://api.example.com/data", None).await?.unwrap();
let data: serde_json::Value = resp.json().await?;

browser.close().await?;

Coverage

Full Python API parity + agent integration. All Playwright Python classes and methods are implemented, plus Browser::bind() / Browser::unbind() (Playwright 1.59) for exposing a Rust-launched browser to external clients like @playwright/mcp, the Playwright CLI, or third-party agent tooling. Chromium, Firefox, and WebKit are tested in CI on Linux, macOS, and Windows.

The remaining path to v1.0 is multi-month dogfooding, API polish, and performance tuning rather than new surface area. See the v1.0 gap analysis for the detailed state of each class.

One known limitation: WebKit launch_persistent_context() fails on native Windows (upstream issue microsoft/playwright#36936, tracked here as #39). Non-persistent WebKit works on Windows; use WSL or macOS/Linux otherwise.

How it works

playwright-rust follows Microsoft's architecture for language bindings:

┌──────────────────────────────────────────────┐
│ playwright-rs (Rust API)                     │
│ - High-level, idiomatic Rust API             │
│ - Async/await with tokio                     │
│ - Type-safe bindings                         │
└─────────────────────┬────────────────────────┘
                      │ JSON-RPC over stdio
┌─────────────────────▼────────────────────────┐
│ Playwright Server (Node.js/TypeScript)       │
│ - Browser automation logic                   │
│ - Cross-browser protocol abstraction         │
│ - Maintained by Microsoft Playwright team    │
└─────────────────────┬────────────────────────┘
                      │ Native protocols
        ┌─────────────┼─────────────┐
        ▼             ▼             ▼
    Chromium      Firefox       WebKit

The crate is a thin JSON-RPC client to the same server the official bindings use, so feature parity and protocol maintenance come from upstream rather than being reimplemented here. The API diverges only where Rust idiom allows a better shape (Result<T>, builders for option-heavy methods, compile-time-validated selectors).

Installation

Add to your Cargo.toml:

[dependencies]
playwright-rs = "0.17"  # Auto-updates to latest 0.17.x
tokio = { version = "1", features = ["full"] }

The default-on macros feature re-exports the locator!() compile-time selector macro. The default-on ring feature selects the crypto backend for driver downloads (and for rustls WebSocket connections); use aws-lc instead when AWS-LC is required:

playwright-rs = { version = "0.17", default-features = false, features = [
    "aws-lc",
    "native-tls",
    "macros",
] }

Disabling default features requires selecting either ring or aws-lc. Other opt-in features are cli (installer binary, see below) and screenshot-diff (pixel-diff assertions). For programmatic trace-zip inspection, add playwright-rs-trace as a [dev-dependencies] entry.

Browser installation (required)

Browsers install once; the library bundles a specific Playwright driver, and each driver release expects matching browser builds. Install through the crate itself so the match is guaranteed and the browser version rides Cargo.lock: copy examples/install-browsers.rs into your project's examples/ directory (it needs tokio with the macros and rt-multi-thread features), then:

cargo run --example install-browsers                          # all browsers
cargo run --example install-browsers -- chromium firefox      # or a subset

Pass --with-deps to also install the system libraries the browsers need (Linux CI typically wants this; it runs the package manager under sudo). Without the flag only browsers install, on every platform, matching npx playwright install.

In CI, the same command runs before the test step:

- name: Install Playwright browsers
  run: cargo run --example install-browsers -- chromium firefox webkit

When dependabot bumps playwright-rs, the crate, driver, and browsers move together with no workflow edit. Never hardcode a Playwright version in a workflow run: step or a package.json: dependabot can't see the former and bumps the latter on npm's cadence, not the crate's. The driver ships its own Node.js runtime, so no setup-node step is needed. For setup scripts and Docker builds, call install_browsers directly. Outside a Cargo project, the cli feature provides an installer binary (cargo install playwright-rs --features cli, then playwright-rs install).

Using with Claude Code / AI agents

This repo ships an Agent Skill so your agent writes against this crate's actual API model instead of guessing from generic Playwright knowledge. It covers adding the dependency and installing browsers, the object model, the builder and locator!() conventions, auto-wait semantics, and capturing a trace when something fails.

npx skills add padamson/playwright-rust

Works with Claude Code, Codex, Cursor, and any other compatible agent.

Claude Code can also install it as a plugin, which tracks this repo:

/plugin marketplace add padamson/playwright-rust
/plugin install playwright-rs@playwright-rust

Third-party marketplaces don't refresh on their own; update with /plugin marketplace update playwright-rust followed by /plugin update playwright-rs@playwright-rust.

The skill lives at skills/playwright-rs-usage/ and points back at docs.rs and the examples for the API surface itself. A build gate keeps it honest: its code compiles against the real crate, and it cannot omit a cargo feature or browser engine the crate exposes.

Development

Building from source, running the test suite, and debugging failures (backtraces, saving a Playwright trace on failure) are covered in docs/development.md.

Star History Chart

Contributing

Contributions should follow Playwright API conventions, include tests, document public APIs with examples, and pass CI (fmt, clippy, tests).

License

Apache-2.0 (same as Microsoft Playwright). Thanks to the Microsoft Playwright team for the framework and the server this crate drives, and to playwright-python for the API design reference.

About

Rust language bindings for Microsoft Playwright

Topics

Resources

Stars

141 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages