Puppeteer Mouse Move Options Explained
Learn what Puppeteer’s `page.mouse.move()` coordinates and `steps` option mean, with runnable examples, common pitfalls, and a screenshot API alternative.
page.mouse.move(x, y, options?) moves Puppeteer’s mouse to the viewport coordinate (x, y). Its only documented move-specific option is steps, which requests the number of movements between the current position and the destination; it defaults to 1. The API does not define steps as a duration, delay, easing curve, random path, or guarantee of human-like movement.
Signature and the steps option
await page.mouse.move(x, y, options?)
x: numeric horizontal destination coordinate.y: numeric vertical destination coordinate.options: optionalReadonly<MouseMoveOptions>.options.steps: optional number of movements from the current position to the destination. Its documented default is1.- Return value:
Promise<void>.
These two calls use the same destination, but request different movement counts:
await page.mouse.move(250, 150);
await page.mouse.move(500, 300, { steps: 10 });
When options is omitted, the default is one movement. Supplying { steps: 10 } requests ten movements from the current mouse position to the target. The reference does not specify how much time passes between them or what path they follow.
Coordinates: viewport CSS pixels
The coordinates are main-frame CSS pixels measured from the viewport’s top-left corner. They are not screen coordinates and are not document coordinates that account for the page’s full scrollable height. A point at (100, 80) means 100 CSS pixels from the viewport’s left edge and 80 CSS pixels from its top edge.
mouse.move() takes coordinates, not a selector. To move toward an element, obtain a point for that element and pass the point’s coordinates. For example, with a locator:
const button = page.locator('button.submit');
const box = await button.boundingBox();
if (!box) {
throw new Error('The button is not visible or has no bounding box');
}
await page.mouse.move(
box.x + box.width / 2,
box.y + box.height / 2,
{ steps: 5 },
);
The bounding box is useful when you specifically need coordinate-based mouse input. For ordinary interaction with a known element, a locator’s own hover or click method is usually simpler because it works with the element rather than requiring you to calculate a point.
Runnable example
This CommonJS example launches Chromium, opens a page, moves the pointer in viewport CSS pixels, and closes the browser even if an operation fails. Install Puppeteer in a project first with npm install puppeteer.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({ width: 800, height: 600 });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
// One movement, because steps is omitted and defaults to 1.
await page.mouse.move(120, 100);
// Request ten movements from the current position to this point.
await page.mouse.move(400, 250, { steps: 10 });
} finally {
await browser.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
The example demonstrates the documented interface only. It does not rely on a particular duration, easing, or trajectory.
Choosing a step count
| Call | Meaning | When it fits |
|---|---|---|
page.mouse.move(x, y) |
Uses the default of one movement. | Directly move the pointer to a coordinate. |
page.mouse.move(x, y, { steps: n }) |
Requests n movements from the current point to the destination. |
You need to specify a movement count for coordinate-based interaction. |
The API reference documents steps as a number but does not describe timing, a maximum, easing, randomness, or human-like behavior. Choose a value for the movement count your task requires; do not use it as a substitute for a delay or as proof that a site will treat the input as human.
Common mistakes and troubleshooting
The pointer appears in the wrong place
Cause: Coordinates were treated as screen pixels or full-document positions. Fix: Use main-frame viewport CSS pixels from the top-left. If the page scrolls, recalculate the target point in the current viewport.
Passing a selector to mouse.move()
Cause: The method expects numeric x and y, not an element or selector. Fix: Use a locator hover method for element-based interaction, or get the element’s bounding box and pass a point inside it.
The target has no bounding box
Cause: The element may not exist, may not be visible, or may not have layout dimensions when the box is read. Fix: Wait for the target to be available and visible, then check for a null bounding box before using its coordinates.
Changing steps does not add a pause
Cause: steps is documented as a count of movements, not a time setting. Fix: If the workflow requires waiting, use an explicit wait that matches the condition you need, such as waiting for an element or application state. Do not infer a duration from the step count.
A site still rejects automated input
Cause: Puppeteer documents mouse events as synthetic and cautions that they do not fully reproduce normal mouse behavior. Fix: Treat this API as browser automation input, not a physical mouse or a guarantee of human-like behavior. If the task is to capture a page, use a screenshot workflow rather than adding pointer movements without a capture-related need.
Performance and reliability
A default move requests one movement; increasing steps requests more movements. The API reference does not provide timing or performance guarantees, so do not assume that a larger count takes a particular amount of time. For reliable coordinate interaction, set the viewport deliberately, wait until the relevant page state exists, and compute coordinates against the current viewport. Prefer element-based locator actions when they express the task directly.
Mouse events remain synthetic. Puppeteer explicitly cautions that they do not fully replicate all normal mouse functionality. A successful call therefore confirms that Puppeteer issued the requested input; it does not establish that every website will interpret it like physical user input.
Or skip the browser setup
If your goal is a screenshot rather than pointer-driven interaction, ScreenshotNeo provides a website screenshot API and MCP server. A single GET request captures a URL; see the ScreenshotNeo API documentation for the available parameters.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) {
throw new Error(`Screenshot request failed: ${res.status} ${res.statusText}`);
}
const image = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', image);
ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and responses identify the page verdict and billing status in headers. Its MCP server includes take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.
Frequently asked questions
Does mouse.move() move to an element?
No. It takes coordinates. Use an element locator action or calculate a point from the element’s bounding box.
Does steps make movement more human-like?
The documented option specifies a number of movements. The API reference makes no claim that it creates human-like motion.
Are the coordinates affected by device pixel ratio?
The Mouse class describes coordinates in main-frame CSS pixels relative to the viewport. Do not substitute physical screen pixels for those coordinates.
What does mouse.move() return?
It returns a promise that resolves with no value, so use await when sequencing it with other browser actions.
References
- Puppeteer
Mouse.move()API reference (version 25.12.0). - Puppeteer
MouseMoveOptionsinterface (version 25.12.0). - Puppeteer Mouse class reference for coordinate basis and the synthetic-event caveat.


