How to Press and Release the Mouse Button With Puppeteer
Use Puppeteer’s `page.mouse.down()` and `page.mouse.up()` to control a mouse button separately. Learn button options, coordinates, limitations, and common fixes.
To press and release a mouse button separately in Puppeteer, call and await page.mouse.down() and page.mouse.up():
await page.mouse.down();
// Run any actions that should happen while the button is held.
await page.mouse.up();
The default button is the left button. To use another supported button, pass the same button option to both calls. Use page.mouse.click(x, y) for a simple coordinate click that does not need a held-down interval.
1. Press and release a button
Each Puppeteer Page has a mouse object. Its down() method presses a button at the current pointer position; up() releases it. Await both calls so the actions happen in order.
await page.mouse.move(300, 200);
await page.mouse.down();
// The left button stays held while these actions run.
await page.waitForTimeout(250);
await page.mouse.up();
The coordinates passed to move() are viewport-relative CSS pixels, with (0, 0) at the top-left of the main frame. If the pointer is not moved first, the press occurs at its current position.
2. Select a mouse button
The default is left. Puppeteer documents these button values: left, right, middle, back, and forward. When pressing a non-default button, pass that choice to both down() and up():
await page.mouse.move(300, 200);
await page.mouse.down({ button: 'right' });
// Perform an action that requires the right button to remain held.
await page.mouse.up({ button: 'right' });
Using the same option makes it clear which button the sequence intends to release. The available API may vary with the Puppeteer version you use; consult its current Mouse API reference if a button value is rejected.
3. Choose between mouse methods and locators
| Need | Use | What it does |
|---|---|---|
| Press and release at the current pointer position, with actions in between | mouse.down() and mouse.up() |
Gives explicit control over the held interval. |
| A simple click at coordinates | mouse.click(x, y) |
Shortcut for moving to the coordinates and performing down/up. |
| Click a known page element | page.locator(selector).click() |
Targets an element and applies locator interaction checks. |
Puppeteer’s guide recommends locators for element-directed interactions. Locators automatically check that the target is in the viewport, visible, enabled, and has a stable bounding box before clicking. Use the mouse API when you need pointer coordinates or explicit press/release timing. See the Puppeteer page interactions guide.
Coordinate click
await page.mouse.click(300, 200);
This is equivalent to moving the pointer to the coordinate and then pressing and releasing the mouse button. Use the separate calls when an action must take place while the button is held.
Element click
await page.locator('button#save').click();
This is usually the clearer choice when the target is an element rather than a point on the screen. A locator click does not give you a held-down interval for arbitrary actions between press and release.
4. Complete runnable example
This Node.js example launches Chromium, opens a page, moves the pointer, presses and releases the left button, and closes the browser. Install Puppeteer with npm install puppeteer, then save this as mouse.js and run node mouse.js.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setContent('<button>Press here</button>');
await page.mouse.move(100, 100);
await page.mouse.down();
await page.waitForTimeout(100);
await page.mouse.up();
} finally {
await browser.close();
}
})();
The example uses synthetic browser mouse events. It demonstrates the API sequence; it does not establish that a particular site will respond to those events as it would to a physical mouse.
5. Important behavior and limitations
- Events are synthetic. Puppeteer’s mouse operations trigger synthetic
MouseEvents and do not reproduce every capability of a physical mouse. - Dragging and text selection have limitations. The Mouse documentation specifically notes that dragging and selecting text are not possible using
page.mouse. Do not assume that holding the button across a move will provide native drag behavior. - Coordinates are viewport-relative. They are main-frame CSS pixels, not document coordinates. Scrolling changes which content appears at a given viewport coordinate.
- Pressing does not choose a target element. The pointer position determines where the event is dispatched. Move it to the intended point first, or use a locator for element interaction.
For details on supported methods and behavior, see the official Puppeteer Mouse API reference.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The page acts as if no click happened | The pointer is at a different coordinate, the target is outside the viewport, or the page state has changed. | Move to the intended viewport coordinate immediately before down(). If the target is an element, prefer a locator click and let it check visibility and stability. |
| The button appears to remain pressed | The sequence did not reach up(), for example because an intervening operation threw an error. |
Put the release in a finally block around the held interval so it runs when an operation fails. |
| A non-left click behaves incorrectly | The press and release use different button options, or a value is unsupported by the installed Puppeteer version. | Pass the same documented button to both calls and check the version’s API reference. |
| Coordinates hit the wrong content | The coordinates are viewport-relative CSS pixels, and scrolling or viewport sizing changed. | Check the current viewport and scroll position; recalculate the point relative to the viewport. |
| A drag or text selection does not work | page.mouse does not fully reproduce physical mouse behavior; Puppeteer documents these limitations. |
Use an interaction supported by the page and automation API, and avoid treating down/move/up as a guaranteed native drag or selection. |
The call fails because page.mouse is unavailable |
The page was not created successfully or the code is using an object other than a Puppeteer Page. | Confirm that page came from browser.newPage() or another valid page creation path. |
A release guarded by finally helps keep the sequence predictable:
await page.mouse.move(300, 200);
await page.mouse.down();
try {
// Actions to run while held.
await doSomething();
} finally {
await page.mouse.up();
}
7. Performance, reliability, and cost
The down/up calls are local browser automation operations; the task itself does not require a paid service or physical mouse hardware. In a larger automation flow, avoid unnecessary fixed delays when the page exposes a condition you can wait for. Keep the release in cleanup logic if intervening work can fail, and prefer locators when you need a reliable element click with Puppeteer’s documented precondition checks.
8. Or skip the browser setup
If your goal is a clean screenshot of a webpage rather than testing mouse behavior, ScreenshotNeo returns a screenshot or PDF from one API request, with no Puppeteer browser setup in your code. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Cookie banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, with no card required.
9. FAQ
Does mouse.down() release the button automatically?
No. Call and await mouse.up() to release it.
Can I use mouse.click() and hold the button?
click() performs the complete move/down/up sequence. Use separate calls when you need work to happen between pressing and releasing.
Are mouse coordinates measured from the page or the screen?
They are CSS pixels relative to the main frame’s viewport, starting at its top-left corner.
Should I use the mouse API for every button click?
No. For a page element, Puppeteer’s guide recommends locators, which check visibility, enabled state, viewport presence, and bounding-box stability.


