Getting Started
The Core Loop
To drive the browser through a task, the rhythm is almost always these four steps:open the page → see what's on it → act on the @ref the snapshot gave you → snapshot again the moment the page changes.
Get this loop flowing, then understand when @ref goes stale and when it self-heals, and you've got ninety percent of chrome-use's day-to-day usage.
The four-step loop
Each step decides based on the result of the last: snapshot -i gives you a structured view containing only interactive elements,
each carrying a short @ref; you take a ref to click / type; the moment the page changes, snapshot again to get fresh refs.
$ chrome-use open <url> # 1. open a page
$ chrome-use snapshot -i # 2. see what's on the page (interactive elements only)
$ chrome-use click @e3 # 3. act on the ref the snapshot gave you
$ chrome-use snapshot -i # 4. the page changed, so snapshot again
The browser stays running between commands, so these feel like one session. When you're done, wrap up with chrome-use close
(or close --all). To read the page, find elements, or interact in more detail, see
Reading the Page, Finding Elements, and Interacting.
$ chrome-use open https://duckduckgo.com
$ chrome-use snapshot -i # find the search box's ref
$ chrome-use fill @e1 "chrome-use cli"
$ chrome-use press Enter
$ chrome-use wait --load networkidle
$ chrome-use snapshot -i # the refs now reflect the search results
$ chrome-use click @e5 # click a result
$ chrome-use screenshot result.png
@ref goes stale: snapshot again the moment the page changes
Refs (@e1, @e2, …) are reassigned on every snapshot.
They go stale the moment the page changes — a navigating click, a form submit, a dynamic re-render, or a modal opening can all invalidate them.
So after a navigation or a structural change, snapshot again before your next ref-based interaction.
chrome-use snapshot -i again and use the new ref.
@ref also self-heals: you don't have to re-snapshot for every tiny DOM change
Each ref records a fingerprint — role + accessible name + ancestor path. When you use it, if the original node is gone,
chrome-use automatically re-locates the matching element on the current page and carries on. So after a
React / Vue list re-render (labels unchanged), click @e3 still hits the right element.
If the element is genuinely gone, it fails loudly rather than clicking the wrong node — it never silently mis-targets. So: you only need to re-snapshot when you've navigated, or when the labels / structure genuinely changed.
snapshot -i again after these.
A plain list re-render (labels unchanged) doesn't need it; let ref self-healing handle it.
Hard rule: snapshot-first, never locate by screenshot
For form fields and buttons, always snapshot -i first, then act on the ref / selector.
Do not use screenshot + coordinate clicks to find or hit elements.
snapshot -i now pierces cross-origin iframes (embedded Google Payments / Stripe / checkout / KYC forms),
listing their elements — along with their input values — by @ref, so click @e / type @e / fill @e
reach straight into the iframe. Only use coordinates for canvas / WebGL, or when snapshot genuinely returns nothing for your target.
Two different intents — only one is discouraged
The rule above targets screenshot-to-locate (using an image to find / hit an element) — that's the bug. Whereas screenshot-to-capture (saving a region or element to a file as reusable image assets — maps, charts, og images, visual-regression baselines, report illustrations) is fully supported and encouraged:
# save a rendered region as a reusable image asset (not for locating)
$ chrome-use screenshot [selector] --clip x,y,w,h <file>
# want to click something a ref can't hit? box gives the element's CSS-pixel box + center
$ chrome-use box @ref
$ chrome-use click <centerX> <centerY>
Screenshots auto-downsample to a longest edge of ≤ 2000px so image readers can take them in and so pixels line up with click x y;
override with --max-width / --max-height / --scale. To click an element a ref can't hit,
box @ref gives its CSS-pixel box and centerX/centerY, which you feed straight to click <centerX> <centerY> — no screenshot needed.
For canvas / WebGL coordinate tricks, see Screenshots · Recording · Viewport and the canvas-related sections.
Two driving paths — and when to switch to eval
What you have in hand is a real Chrome with the user's real DOM. There are two layers, and you can mix them freely:
snapshot + @ref, find, typed actions. Convenient and readable, good for straightforward forms and navigation.
But the accessibility view is lossy and fragile: refs go stale on any change, hidden inputs never appear, and overlays block coordinate clicks.
chrome-use eval "<js>" — your eyes and hands on the real DOM: read hidden inputs, reach into Shadow DOM / iframes,
inspect form.elements and .validity, extract the exact structure you want, or just el.click().
eval instead of retrying it over and over —
it's the fastest way to find out "why it failed" (e.g. a hidden point_choice=none the UI never exposes).
Don't try a flaky structured locator three times; drop straight to eval and act on the DOM.
# list every field's name / type / value / checked in the form
$ chrome-use eval "[...document.forms[0].elements].map(e=>[e.name,e.type,e.value,e.checked])"
# read a hidden field the UI never exposes
$ chrome-use eval "document.querySelector('[name=point_choice]')?.value"
# find every field that fails validation and its error message
$ chrome-use eval "[...document.forms[0].elements].filter(e=>!e.validity.valid).map(e=>e.name+': '+e.validationMessage)"
# click directly via the DOM, bypassing overlays
$ chrome-use eval "document.querySelector('#stubborn').click()"
The escalation ladder is: for straightforward pages, snapshot + @eN is fastest → when you don't want to snapshot, use
find role/text/label → native CSS selectors → the moment any of the above fights you, go to eval
and act on the DOM directly. For more locating techniques, see Finding Elements.
eval runs by default in the top document's main world, and state persists across calls — so a top-level const x / let x
will collide with the next call (Identifier 'x' has already been declared). Use unique names, attach to window.x,
or wrap the body in an IIFE. For quotes / non-ASCII / large scripts, use eval --stdin (heredoc),
eval --file <path>, or eval -b <base64> instead of an inline form the shell will mangle.
Next steps
Once the core loop is flowing, dive deeper as needed:
The various snapshot flags, get text, cross-frame reads, and piercing closed Shadow DOM.
click, fill, type, select, pick to drive a real browser.
find role/text/label and @ref to lock onto a target precisely.
Not set up yet? Install the CLI, install the Chrome extension, pull the agent skill.