playwright-cli Browser Automation
github/gh-aw
Drives a real browser from the command line with playwright-cli to open pages, interact, mock requests, save state and work with Playwright tests.
Procedural reference for using playwright-rs in Rust browser-automation code — object model (Browser/Context/Page/Locator), the locator!() macro, builder pattern for options, auto-wait semantics…
$ npx skills add padamson/playwright-rust --skill playwright-rs-usage -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install padamson/playwright-rust playwright-rs-usage --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/padamson/playwright-rust.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/playwright-rs-usage .claude/skills/playwright-rs-usage && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "playwright-rs-usage" agent skill from https://github.com/padamson/playwright-rust/tree/main/skills/playwright-rs-usage into .claude/skills/playwright-rs-usage/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-rs-usage", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/padamson/playwright-rust/tree/main/skills/playwright-rs-usageType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add padamson/playwright-rust --skill playwright-rs-usage -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install padamson/playwright-rust playwright-rs-usage --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/padamson/playwright-rust.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/playwright-rs-usage .agents/skills/playwright-rs-usage && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "playwright-rs-usage" agent skill from https://github.com/padamson/playwright-rust/tree/main/skills/playwright-rs-usage into .agents/skills/playwright-rs-usage/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-rs-usage", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add padamson/playwright-rust --skill playwright-rs-usage -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install padamson/playwright-rust playwright-rs-usage --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/padamson/playwright-rust.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/playwright-rs-usage .cursor/skills/playwright-rs-usage && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "playwright-rs-usage" agent skill from https://github.com/padamson/playwright-rust/tree/main/skills/playwright-rs-usage into .cursor/skills/playwright-rs-usage/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-rs-usage", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/padamson/playwright-rust.git --path skills/playwright-rs-usage--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add padamson/playwright-rust --skill playwright-rs-usage -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install padamson/playwright-rust playwright-rs-usage --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/padamson/playwright-rust.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/playwright-rs-usage .gemini/skills/playwright-rs-usage && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "playwright-rs-usage" agent skill from https://github.com/padamson/playwright-rust/tree/main/skills/playwright-rs-usage into .gemini/skills/playwright-rs-usage/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-rs-usage", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install padamson/playwright-rust playwright-rs-usageInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add padamson/playwright-rust --skill playwright-rs-usage -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/padamson/playwright-rust.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/playwright-rs-usage .github/skills/playwright-rs-usage && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "playwright-rs-usage" agent skill from https://github.com/padamson/playwright-rust/tree/main/skills/playwright-rs-usage into .github/skills/playwright-rs-usage/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-rs-usage", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add padamson/playwright-rust --skill playwright-rs-usage -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install padamson/playwright-rust playwright-rs-usage --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/padamson/playwright-rust.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/playwright-rs-usage .opencode/skills/playwright-rs-usage && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "playwright-rs-usage" agent skill from https://github.com/padamson/playwright-rust/tree/main/skills/playwright-rs-usage into .opencode/skills/playwright-rs-usage/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-rs-usage", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
playwright-rs-usageProcedural reference for using playwright-rs in Rust browser-automation code — object model (Browser/Context/Page/Locator), the locator!() macro, builder pattern for options, auto-wait semantics…
Playwright Rs Usage is an agent skill from padamson/playwright-rust. Procedural reference for using playwright-rs in Rust browser-automation code — object model (Browser/Context/Page/Locator), the locator!() macro, builder pattern for options, auto-wait semantics, adding the crate and installing its browsers, and how to capture / inspect traces for failure diagnosis. Use when writing tests or scripts with playwright-rs as a dependency. Loaded automatically when the current repo has playwright-rs in its Cargo.toml.
Its SKILL.md is about 5.3k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Testing & QA, covering Browser testing and Browser automation. It works with Playwright and Rust. The repository describes itself as: Rust language bindings for Microsoft Playwright. The licence is Apache-2.0.
Read from SKILL.md and the folder at commit 0cf5194. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
cargonpxplaywrightFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
docs.rsplaywright.devtrace.playwright.devFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Playwright Rs Usage loads about 5.3k tokens when it runs. Until then it costs about 118 tokens; SKILL.md has 2,366 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from padamson/playwright-rust at commit 0cf5194, republished under its Apache-2.0 licence (© padamson). 2,366 words, ~5,315 tokens.
.claude/skills/playwright-rs-usage/SKILL.md (or your agent's skills folder).Rust bindings for Microsoft Playwright. This crate is a thin JSON-RPC client to the upstream Playwright server, so the API mirrors playwright-python / java / .NET semantics. When unsure about a method's behavior, the upstream Playwright docs are the authoritative reference.
Drift discipline. The canonical API tour lives in the crate-level rustdoc at https://docs.rs/playwright-rs — that's compile-checked against the actual code. This skill is the "what to reach for, what to avoid" overlay: durable conventions, not a method-by-method reference. Two build gates hold it to the crate: the Rust code block below is compiled against the real API, and every cargo feature and browser engine the crate exposes must be named somewhere in this file, so a capability cannot ship here undocumented.
Neither gate checks that this prose is accurate. That residue is real. The text is written in concepts (auto-wait, builder pattern) rather than specific method names so it ages slowly, but when it and the crate disagree, the crate wins: check https://docs.rs/playwright-rs for the version you actually have.
Two things, and the second one is the one that surprises people.
The crate. playwright-rs in [dependencies], or [dev-dependencies]
if it is test-only, alongside tokio with the full feature. Defaults are
native-tls, macros, ring, and route-service: transport, the
locator!() macro, the crypto backend, and in-process serving through
route_service.
Prefer leaving them on. default-features = false is only needed to swap
ring for aws-lc, and it drops every default, so the replacements have to
be listed back explicitly or locator!() disappears and no TLS transport
remains:
playwright-rs = { version = "0.18", default-features = false, features = [
"aws-lc",
"native-tls",
"macros",
"route-service",
] }Three capabilities are opt-in and off unless asked for. screenshot-diff
turns on pixel-diff screenshot assertions, so reach for it when the task
calls for comparing a rendering against a baseline rather than asserting on
the DOM. trace re-exports the trace-zip parser as playwright_rs::trace,
for code that reads back the traces it recorded. cli builds an installer
binary for use outside a Cargo project;
inside one, prefer the example below, because cargo install compiles a
second copy of the crate that then has to be kept in sync with the
project's lockfile.
The browsers, which are a separate install and are required. A fresh
checkout that only adds the dependency will fail at launch. The crate
bundles one pinned Playwright driver and each driver expects matching
browser builds, so install them through the crate rather than through a
global npx playwright install. That way the browser version rides
Cargo.lock and cannot drift from the driver. Copy
examples/install-browsers.rs
into the project's examples/ and run it once:
cargo run --example install-browsers # all
cargo run --example install-browsers -- chromium firefox # or a subsetThe same line belongs in CI before the test step. Never pin a Playwright
version in a workflow or a package.json: dependabot cannot see the
former and bumps the latter on npm's cadence rather than the crate's, and
either way the driver and browsers stop matching. The driver ships its own
Node runtime, so no setup-node step is needed. In a setup script or
Dockerfile, call
install_browsers
directly instead.
On Linux the browsers also need system libraries: pass --with-deps to
the example (CI usually wants this; it runs the package manager under
sudo), or call install_browsers_with_deps instead. Without it only
browsers install, on every platform — the driver prints a missing-library
box and still exits 0, as npx playwright install does, so the failure
surfaces later as a browser that will not launch. Two traps worth naming:
cargo run --example only resolves examples in the current package, so a
consumer copies the file into their own examples/ first; and Linux is not
special-cased, so a call that omits the flag there installs no libraries.
The driver, which the build script downloads. The Playwright server and its Node runtime, about 130 MB, are assembled when the crate first compiles, so a fresh build needs network access and a browser launch never does. Every job that compiles the crate pays for it, including ones that only lint or run unit tests, and a mutation runner that builds in several copies pays several times. Two environment variables fix that, set at the job level because a value that differs between steps reruns the build script:
jobs:
lint: # compiles the crate, launches nothing
env:
PLAYWRIGHT_SKIP_DRIVER_DOWNLOAD: "1"
test: # launches browsers
env:
PLAYWRIGHT_DRIVER_CACHE_DIR: ${{ github.workspace }}/.playwright-driver
steps:
- uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.playwright-driver # beside the browsers
# The lockfile hash moves on every playwright-rs bump. An
# unversioned key keeps hitting after a bump, so the new driver is
# downloaded every run and never saved.
key: ${{ runner.os }}-playwright-driver-${{ hashFiles('**/Cargo.lock') }}The same skip knob belongs in a local mutation-testing script. A failed download does not fail the build: cargo hides a dependency's build warnings, so the first sign is the launch error, and a lib-only gate never notices at all.
Playwright start here — Playwright::launch().await?
└── BrowserType .chromium() / .firefox() / .webkit()
└── Browser .launch().await? → owns the browser process
└── BrowserContext isolated cookies / storage
└── Page one tab
└── Locator selector with auto-waitLocator is the workhorse. Build with page.locator(...) or the
semantic get_by_* helpers (get_by_role, get_by_text, etc. —
see docs.rs for the full list) and chain action / assertion methods.
Result<T> and async/await on tokio. One error type:
playwright_rs::Error. Use ? to propagate.goto, click,
screenshot, fill, tracing().start, etc. take an Options
struct. These are #[non_exhaustive] (so upstream option additions
stay non-breaking) — struct literals won't compile. Construct with
the type's builder() where it has one, otherwise chain setters off
Default/new(): GetByRoleOptions::default().name("OK").exact(true),
Cookie::new(name, value).domain("example.com"). The exact method
names live on docs.rs; don't memorize them.expect() assertions retry until they hold
or time out. Never insert tokio::time::sleep between an action
and a check — that's a smell. If a wait feels necessary, you
probably want expect(locator).to_be_visible().await or similar.locator!() macro for literal selectors. Compile-time validation
catches typos and structural errors. Fall back to &str only for
selectors computed at runtime.This block is compile-checked by cargo xtask verify-agent-docs —
if the API drifts, the verifier fails:
use anyhow::Result;
use playwright_rs::{Playwright, locator, expect};
#[tokio::test]
async fn login_flow() -> Result<()> {
let pw = Playwright::launch().await?;
let browser = pw.chromium().launch().await?;
let context = browser.new_context().await?;
let page = context.new_page().await?;
page.goto("https://example.com/login", None).await?;
page.locator(locator!("input[name='user']")).fill("alice", None).await?;
page.locator(locator!("input[name='pass']")).fill("hunter2", None).await?;
page.locator(locator!("text=Sign in")).click(None).await?;
expect(page.locator(locator!(".welcome"))).to_be_visible().await?;
browser.close().await?;
Ok(())
}Under cargo nextest, browser tests fail out of the box for reasons that
have nothing to do with the test. Add .config/nextest.toml to the
consuming project before writing many of them:
[profile.default]
# A browser and its driver take longer than nextest's 100ms default to be
# reaped after a test ends. Without this, passing tests are intermittently
# flagged "leaky" and fail the run.
leak-timeout = "1s"
# Launching a browser is slow, and Firefox and WebKit are slower than
# Chromium. Worst on Windows.
slow-timeout = { period = "30s", terminate-after = 2 }
[profile.ci]
leak-timeout = "1s"
slow-timeout = { period = "60s", terminate-after = 2 }
retries = 1leak-timeout is the one that will otherwise cost an afternoon: the
symptom is an intermittent "leaky" failure on a test whose assertions all
passed, which reads like a bug in the test.
Raise slow-timeout further for tests that drive Firefox or WebKit
specifically, using a [[profile.default.overrides]] block with a filter.
Reach for retries only in CI, where a retried browser launch is cheaper
than a re-run; locally it hides flakes you want to see.
Teardown itself is already synchronous: dropping Playwright blocks until
the driver exits, closing every browser cleanly rather than signalling them
(which would truncate in-flight traces, videos and HARs). In async code
prefer playwright.shutdown().await, which does the same work without
blocking a runtime thread.
Concept-level pointers; the exact options live on docs.rs.
Stable / redacted screenshots. ScreenshotOptions carries
animations(Disabled) for flake-free shots (freeze CSS animations
before capture) and mask/mask_color to overpaint dynamic or
sensitive elements. Reach for animations(Disabled) whenever a
screenshot races an animation.
Context-level events. Beyond per-page handlers, BrowserContext
observes activity across all its pages — on_download,
on_page_load / on_page_close,
on_frame_attached / _detached / _navigated — and
Browser::on_context fires for each new context. Use these for
multi-tab fixtures instead of wiring every page individually.
HAR network capture. tracing().start_har(path, ..) /
stop_har() records all network traffic to a HAR — inspect it in
browser devtools or replay it deterministically with
route_from_har. A sibling to trace capture.
Typed page probes. page.evaluate::<R, T>(expr, arg)
deserializes the JS return value straight into any serde
Deserialize type — define a struct for the shape and skip manual
parsing (None::<&()> for the no-argument case). evaluate_value
(returns String) is only for one-off scalar probes; if you catch
yourself returning delimited strings from JS and splitting them in
Rust, switch to evaluate. Runnable walkthroughs:
examples/evaluate_typed.rs and examples/canvas_pixels.rs (canvas
pixel assertions for wasm/canvas frontends).
Drags: Locator::drag_to covers everything, including canvas.
It drives the real pointerdown → capture → pointermove →
pointerup chain (works against setPointerCapture UIs), and
DragToOptions::target_position — an offset from the target's
top-left — turns it into "drag to a coordinate": pass the containing
canvas/stage as the target. Prefer it over held-button
Mouse::move_to sequences, which hang on headless Linux. Don't
hand-roll synthetic PointerEvent dispatch via evaluate.
External drag-and-drop. Locator::drop simulates dragging files
or data in from outside the page (upload zones), distinct from
drag_to, which drags one element onto another within the page.
File System Access API flows. page.fake_file_system() installs
an opt-in fake of showSaveFilePicker/showOpenFilePicker (native
dialogs no automation tool can drive): seed opens with
set_open_file, assert saves with last_saved_bytes(), control
permission state. Don't hand-roll an add_init_script picker shim.
Accessibility-tree assertions. expect_page(&page) .to_match_aria_snapshot(..) (and the locator form) guard the page's
ARIA structure as a regression check; aria_snapshot can emit
[box=..] bounding boxes for visual/agent reasoning.
aria_snapshot_json returns the same tree as serde_json::Value
rather than YAML markup, which is what an agent walking the tree
wants; the YAML form is for a human reading a diff.
Pages behind HTTP auth. BrowserContextOptions::builder() .http_credentials(vec![HttpCredentials::new("user", "pass")]) — do not
hand-set an Authorization header. Give an entry an origin when the
test talks to more than one host; the first matching entry wins.
APIRequestContextOptions takes the same list, and is the only one that
honors send(HttpCredentialsSend::Always) — which you need when the
server answers 403 rather than 401, leaving nothing to react to.
Dialogs: wait for the close, not the open. on_dialog hands you
the dialog to accept or dismiss; on_dialog_closed fires once it has
been answered, which is the point at which the page is interactive
again. Both exist on Page and BrowserContext, and context handlers
run first.
Visibility is a locator, not a pseudo-class. locator.visible()
narrows to the visible matches (Playwright's replacement for
:visible), and filter(FilterOptions::default().visible(false)) is
the hidden half.
Frames without naming the iframe. page.frame_locator(None)
searches every frame in the subtree, so no iframe selector is needed;
Frame::frame_locator takes the same optional argument. The rest of
the locator resolves inside one frame, and matching several is an
error, so keep the inner selector unique.
Waiting on arbitrary state: wait_for_function. When there is no
selector to wait on — a JS flag, a store, a counter —
page.wait_for_function("() => window.app?.ready", None) polls a
predicate and resolves to its value as a JSHandle. The locator form
binds the matched element as the first argument
(locator.wait_for_function("el => el.dataset.state === 'done'", None))
and returns (). Prefer this over poll_until-around-evaluate
loops: the driver polls on requestAnimationFrame and enforces the
timeout. WaitForFunctionOptions::polling_interval switches to timer
polling (page-global form only).
Calling back into Rust: evaluate_with_callback. Hands the
expression a Rust closure as its argument — JS calls it, the
arguments come to Rust, and the closure's return value resolves the
JS promise. The expression may stash the function for later (event
listeners); the binding lives until the page closes, so register once
rather than in a loop. For a permanently installed window.fn, use
expose_function/expose_binding instead.
Session save & replay. context.storage_state(None) captures
cookies and per-origin storage;
StorageStateOptions::default().credentials(true).indexed_db(true).opfs(true)
additionally captures WebAuthn passkeys, IndexedDB, and each origin's
private file system.
set_storage_state(state) restores into any context — a replace,
not a merge: the driver clears storage for every visited origin, and
restoring a state without credentials disposes an installed virtual
authenticator. The fast path for "log in once, reuse everywhere".
Opting out of auto-scroll. Pointer actions scroll the target into
view before acting; ClickOptions::builder().scroll(Scroll::None)
makes the action fail instead — the way to assert something is
already visible, or to avoid scroll side effects.
Serving the app from inside the test: route_service (the release
0.18.0 and later). When the frontend under test is served by Rust (an axum Router, or a built
wasm bundle in a directory via tower-http's ServeDir), hand the
service to page.route_service("https://app.example/**", service) or
the BrowserContext form and navigate to that origin. Every matching
request is answered by the service in-process: no listener, no port,
any origin including https://, works in sandboxes. Prefer it over
binding an ephemeral port and spawning axum::serve for a test. Reach
for a real listener instead when the app depends on streaming
responses, server-sent events, WebSockets (those go through
route_web_socket), or connection-level behavior, since route
interception delivers whole bodies and has no connection. The
Sleeping to wait for a page is wrong anyway, and wronger here: a delay
tuned against a real listener can be too short once the bundle crosses
the driver channel, which is the one behavior change a consumer hit
converting a suite. Wait on what the app renders. The
route_service module rustdoc has the full contract and a wasm
testing section.
Rust has no async Drop, so trace cleanup is explicit. The
canonical pattern: capture the test result, run cleanup
unconditionally, pass the trace path only on failure.
See examples/trace_on_failure.rs
for the runnable end-to-end version — it's compiled by
cargo check --examples so it can't silently rot.
To view a captured trace: playwright show-trace trace.zip. The
viewer is language-agnostic — same UI JS / Python users see. The
hosted version is at https://trace.playwright.dev.
For CI bots, agent feedback loops, or any code that wants to read what
happened in a trace without re-running the test, turn on the trace
feature and the parser is playwright_rs::trace, at a version this crate
already pins:
playwright-rs = { version = "0.18", features = ["trace"] }Use trace::open(path) then TraceReader::actions() to walk the
reassembled action stream, TraceReader::network() for HTTP traffic, and
TraceReader::blob(path) to pull a screencast frame or response body out
of the archive. Code that reads traces without driving a browser can
depend on playwright-rs-trace alone instead; it needs 0.2 or later to
read traces from the current driver, since 0.1 reads only the older
trace format and refuses them. The crate's //! rustdoc on
https://docs.rs/playwright-rs-trace has a runnable example.
sync_playwright. Async only; everything awaits on tokio.Result, not exceptions. Use ? to propagate.Options structs with
..Default::default(), not name=value in method calls.Drop. Always close browsers / stop tracing explicitly
in a cleanup block — don't rely on RAII for I/O.page.locator(...)
returns a Locator you can .clone() and re-use cheaply.evaluate_with_callback) since serialized data cannot carry a Rust
closure.expect(..) and let it
auto-retry.tracing.stop() — traces are written
on stop, so order matters: stop tracing first, then close.&str. Switch to locator!() for any
selector you write as a literal.locator!() macro: https://docs.rs/playwright-rs-macros© padamson, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/playwright-rs-usage of padamson/playwright-rust.
Open the folder on GitHubat commit 0cf5194
Playwright Rs Usage next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Playwright Rs Usage this skillpadamson/playwright-rust | 153 | — | ~5.3k | Automated safety check: Pass | Apache-2.0 | |
| playwright-cli Browser Automationgithub/gh-aw | 5.4k | 24 repos | ~2.8k | Automated safety check: Pass | MIT | |
| Playwright Skilllackeyjb/playwright-skill | 3.2k | — | ~1.9k | Automated safety check: Pass | MIT | |
| Playwright CLItestdino-hq/playwright-skill | 386 | 1 repos | ~2.5k | Automated safety check: Pass | MIT | |
| Playwright Component Testingmellowagain/gitarena | 115 | 1 repos | ~2.6k | Automated safety check: Pass | MIT | |
| Playwright Bot Bypassgreekr4/playwright-bot-bypass | 200 | — | ~3.4k | Automated safety check: Pass | MIT |
github/gh-aw
Drives a real browser from the command line with playwright-cli to open pages, interact, mock requests, save state and work with Playwright tests.
lackeyjb/playwright-skill
Complete browser automation with Playwright. An agent skill from lackeyjb/playwright-skill.
testdino-hq/playwright-skill
Automates browser interactions for testing and validating your own web applications using playwright-cli.
mellowagain/gitarena
Set up component testing with Playwright using a story gallery — scaffold stories and a gallery dev page driven by the built-in mount fixture, no dedicated component-testing runtime.
greekr4/playwright-bot-bypass
This skill should be used when the user asks to "bypass bot detection", "avoid CAPTCHA", "stealth browser automation", "undetected playwright", "bypass Google bot check", "rebrowser-playwright", or…
tingly-dev/tingly-box
Capture headless-Chrome screenshots of the tingly-box frontend (running locally in mock mode) so frontend changes can be visually verified in environments without a real browser.
padamson/playwright-rust
Conventions for authoring rustdoc doctests in playwright-rust — the norun annotation, module-level placement, hidden scaffolding lines, and how doctests are exercised in CI vs pre-commit.
padamson/playwright-rust
End-to-end release runbook for playwright-rust — version bump, supply-chain refresh, per-crate CHANGELOGs, tag-prefix routing for the three workspace crates, the safer push-then-tag workflow that…
padamson/playwright-rust
Procedure for keeping playwright-rust's cargo audit / cargo deny / cargo vet checks green when bumping the project's own version, when external crates change, and when a security advisory drops.
Works with
Categories
Procedural reference for using playwright-rs in Rust browser-automation code — object model (Browser/Context/Page/Locator), the locator!() macro, builder pattern for options, auto-wait semantics…. Playwright Rs Usage is an agent skill from padamson/playwright-rust.() macro, builder pattern for options, auto-wait semantics, adding the crate and installing its browsers, and how to capture / inspect traces for failure diagnosis.
Playwright Rs Usage fits situations like: scripts with playwright-rs as a dependency; tasks that involve Browser testing; tasks that involve Browser automation.
Run `npx skills add padamson/playwright-rust --skill playwright-rs-usage -a claude-code`. Or copy the skill folder (skills/playwright-rs-usage in padamson/playwright-rust) into .claude/skills/playwright-rs-usage in your project. Claude Code loads it when a task matches its description.
Run `npx skills add padamson/playwright-rust --skill playwright-rs-usage -a codex`. Or copy the skill folder (skills/playwright-rs-usage in padamson/playwright-rust) into .agents/skills/playwright-rs-usage in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add padamson/playwright-rust --skill playwright-rs-usage -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/playwright-rs-usage, .gemini/skills/playwright-rs-usage, .github/skills/playwright-rs-usage and .opencode/skills/playwright-rs-usage in your project.
Going by SKILL.md and its folder, Playwright Rs Usage needs the command-line tools its instructions call (cargo, npx and playwright). Our summary lists: Python 3; Node.js.
SKILL.md names 3 domains. As links in the text: docs.rs, playwright.dev and trace.playwright.dev. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Playwright Rs Usage is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.3k tokens (SKILL.md is roughly 21k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Playwright Rs Usage: playwright-cli Browser Automation (github/gh-aw, 5.4k stars), Playwright Skill (lackeyjb/playwright-skill, 3.2k stars), Playwright CLI (testdino-hq/playwright-skill, 386 stars) and Playwright Component Testing (mellowagain/gitarena, 115 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
padamson (a GitHub user) maintains it in padamson/playwright-rust, which has 153 GitHub stars. The repository holds 4 skills in this directory. The repository was last updated on October 4, 2026.
Source: padamson/playwright-rust on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.