Puppeteer Click Options: How to Configure Clicks
Learn how Puppeteer click options control buttons, click count, press duration, and click position, plus how to handle dynamic elements and navigation safely.
Puppeteer accepts click settings as the second argument to page.click(selector, options) or as the argument to elementHandle.click(options). The options control the mouse button, number of clicks, press duration, click position, and an experimental debug highlight. For changing pages, start the navigation wait and click together with Promise.all().
This guide covers Puppeteer’s documented click options, reliable patterns for dynamic pages, and common failure cases. The cited API reference displays Puppeteer version 25.12.0; check your installed version’s types if you need exact type details.
1. Install Puppeteer and run a click
Install Puppeteer in a Node.js project, then launch a browser, open a page, and click a selector:
npm install puppeteer
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com');
await page.click('a');
console.log('Clicked the first matching link');
} finally {
await browser.close();
}
})();
page.click() finds the selector, scrolls the matching element into view when needed, and clicks its center. If several elements match, it clicks the first. If none match, the promise rejects. Use a selector specific enough to identify the intended control.
For the full API contract, see Puppeteer’s Page.click() reference.
2. Configure the click options
ClickOptions extends Puppeteer’s mouse click options. These are the documented controls:
| Option | What it controls | Notes |
|---|---|---|
button |
Which mouse button to use. | See the installed version’s mouse option types for the accepted values. |
count |
Number of clicks to perform. | Defaults to 1; a value above one produces repeated clicks, such as a double-click. |
delay |
Time in milliseconds between mouse press and release. | This is how long the button remains pressed, not a pause before clicking. |
offset |
Position within the element to click. | Relative to the top-left corner of the element’s border box. Check the installed type definitions for the exact object shape. |
debugHighlight |
Temporarily highlights the click location. | Experimental; may not work on every page and does not persist across navigation. |
Example using the documented mouse options:
await page.click('button.submit', {
button: 'left',
count: 1,
delay: 100,
});
For a double-click, set count: 2. For a click away from the center, pass offset using the shape defined by your installed Puppeteer types; the API reference establishes that it is relative to the element’s border box. Avoid copying an offset object shape from another version without checking the local type definition.
See the official references for ClickOptions and MouseClickOptions.
3. Choose Page.click, ElementHandle.click, or a locator
| Situation | Use | Behavior to account for |
|---|---|---|
| You have a selector and want a direct click. | page.click(selector, options) |
Scrolls into view, clicks the center, and chooses the first match. |
| You already have an element handle. | handle.click(options) |
The handle can become detached if the DOM changes; clicking a detached handle throws. |
| The interface is dynamic and needs readiness checks. | page.locator(selector).click() |
Locators provide configurable preconditions and a per-locator timeout. |
Use a locator when visibility, enabled state, viewport presence, or a stable bounding box matters. The page-interactions guide describes configuring these checks. A waitForSelector() call only waits for DOM availability; it does not automatically retry an action if that action subsequently fails.
const submit = page.locator('button.submit');
await submit.click();
For locator configuration details and the available preconditions, consult Puppeteer’s page interactions guide. Keep the locator timeout aligned with how long the page is expected to take, rather than adding arbitrary delays.
When you need a handle, acquire it near the click so it is less likely to go stale:
const button = await page.$('button.submit');
if (!button) {
throw new Error('Submit button was not found');
}
await button.click();
The handle method also scrolls the element into view when needed and clicks its center. See ElementHandle.click().
4. Wait correctly when a click triggers navigation
Do not wait for navigation only after the click: navigation may begin before the wait is registered. Start both operations together:
const [response] = await Promise.all([
page.waitForNavigation(),
page.click('a.next'),
]);
console.log('Navigation response:', response?.status());
If needed, pass navigation wait options to waitForNavigation() and click settings to page.click():
const [response] = await Promise.all([
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
page.click('button.continue', { delay: 50 }),
]);
Select a navigation condition that matches the page. A click that updates content in place may not cause a navigation at all; in that case wait for the expected UI state with a locator or another page condition instead. The documented race-safe pattern is covered in the Page.click() reference.
5. Make clicks dependable on dynamic pages
- Target a unique element. Prefer a stable selector for the intended button or link. Remember that
page.click()uses the first match if the selector is ambiguous. - Wait for the interaction’s real preconditions. For dynamic interfaces, locator checks can cover visibility, enabled state, viewport presence, and a stable bounding box. Use a locator timeout appropriate to the page.
- Avoid stale handles. If a framework replaces a node during rendering, reacquire the handle or use a locator that resolves the target for the interaction.
- Pair navigation and click waits. Put them in the same
Promise.all()to avoid missing the navigation event. - Use offsets only when necessary. A center click is the default. An offset can target a specific area, but it depends on the element’s dimensions and position.
These patterns make failures easier to diagnose: they distinguish a missing element, an element that is present but not ready, a stale handle, and a click that started navigation before its wait.
6. Troubleshoot common click failures
| Symptom | Likely cause | Fix |
|---|---|---|
page.click() rejects because the selector was not found. |
The element is not yet in the DOM, or the selector is wrong. | Check the selector and wait for the relevant element. For dynamic interactions, prefer a locator with suitable readiness checks. |
| The wrong matching control is clicked. | The selector matches multiple elements; page.click() selects the first. |
Narrow the selector so it identifies the intended control. |
ElementHandle.click() throws after a page update. |
The referenced element detached from the DOM. | Acquire a fresh handle or use a locator for the interaction. |
| The click happens but the navigation wait times out. | The page changed in place without navigation, or the wait began too late. | Start wait and click together with Promise.all(). If there is no navigation, wait for the resulting UI state instead. |
| A click lands in the wrong part of an element. | An offset was supplied for a different element size or interpreted with an incorrect type shape. | Use the center click unless a specific point is required; check the installed Offset type and calculate the position relative to the border box. |
| The debug highlight is absent or disappears. | debugHighlight is experimental, may not work on the page, and does not persist through navigation. |
Use it only as a temporary diagnostic aid; do not make automation depend on it. |
7. Performance, reliability, and cost
A click itself is a small interaction; the larger time and reliability costs usually come from waiting for the page, the target, or a resulting navigation. Avoid fixed sleeps when a meaningful readiness condition is available. Locators can wait on configured preconditions, while a navigation-triggering click should be paired with the navigation wait.
There is no universal delay or timeout that fits every site. Use a press duration only when the page requires it, and avoid repeated clicks unless the control expects them: a second click can submit a form twice or activate a different state. Keep browser lifecycle management explicit and close the browser in a finally block so errors do not leave it running.
Puppeteer click configuration has no per-click service charge; operational cost comes from running and maintaining the browser environment, including compute and any surrounding infrastructure. The documentation reviewed does not provide a benchmark or universal success rate, so measure your own page flows if timing or throughput matters.
8. Capture a page without managing a browser
When the task is to save a page image or PDF rather than interact with its controls, ScreenshotNeo can return a screenshot or PDF from one GET request. It is a website screenshot API and MCP server for developers. See the ScreenshotNeo API documentation for request options.
Or skip the browser setup
Request a screenshot with cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
And Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with response headers indicating the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.
9. Frequently asked questions
Does page.click() click the center of an element?
Yes. It scrolls the matched element into view when necessary and clicks its center unless an offset changes the click position.
Does delay pause before clicking?
No. It sets the time in milliseconds between mouse press and release.
What happens if a selector matches several elements?
page.click() clicks the first match. Make the selector more specific when order is not a reliable way to identify the target.
When should I use a locator instead of waitForSelector()?
Use a locator when the action needs readiness checks such as visibility, enabled state, or a stable bounding box. Waiting for a selector alone confirms DOM availability, not that a later action will succeed.
Can click options make a click trigger navigation?
The options configure the mouse action. When that action causes navigation, separately wait for it with Promise.all([page.waitForNavigation(), page.click(...)]).


