Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If a Puppeteer click works only on some runs, first replace a bare selector click with Puppeteer’s recommended locator API: await page.locator(selector).click();. A locator waits for the target to be in the viewport, visible, enabled, and stable across two animation frames before clicking. If the action navigates, start the navigation wait and click together with Promise.all. These steps address common timing and navigation races, but the title alone cannot identify the cause in a particular script.

Start with a locator click

Puppeteer recommends locators for selecting and interacting with elements. A locator click checks whether the target is in the viewport, visible, enabled, and has a stable bounding box across two consecutive animation frames. That is more than checking that a matching element exists in the DOM.

Use a selector that identifies the intended control:

const selector = 'button.submit';
await page.locator(selector).click();

Replace button.submit with a selector that matches the page you are automating. Puppeteer’s guide documents CSS selectors as well as text, accessibility attributes, XPath, and shadow-root traversal. If several controls match, inspect the page and make the selector more specific; a locator cannot infer which repeated button you meant. See the Puppeteer Page interactions guide.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What the locator checks do—and do not—tell you

The checks make a click less dependent on guessing a fixed delay before the control is ready. They do not prove that the application accepted the action or reached the state you wanted. A click promise resolving is not the same as a form submission succeeding, a menu opening, or a route changing; wait for the page-specific outcome as well.

Locator APIs and defaults can vary by installed Puppeteer version. Check the API documentation for the version in your project before relying on a particular method or timeout default.

Handle clicks that trigger navigation

A common intermittent failure is a navigation race: the script clicks first and only then begins waiting for the navigation, which may already have started. Start both operations together. Puppeteer documents this pattern for page.click:

const [response] = await Promise.all([
  page.waitForNavigation(),
  page.locator('a.next-page').click(),
]);

Use a selector for the actual control. The locator click is the action; the navigation wait is registered before it can trigger the navigation. The returned response may be null for some navigation types, so do not treat a non-null response as the only proof of success. Puppeteer counts History API URL changes as navigation as well. Consult the Page API documentation and choose navigation options appropriate to the expected page behavior.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When not to wait for navigation

Do not add waitForNavigation() to every click. If the interaction updates the current page without a navigation Puppeteer recognizes, the wait may time out even though the click worked. Instead, wait for the application’s observable result—for example, a success message, a changed URL when appropriate, or a newly displayed panel. The exact condition depends on the application.

Choose the right kind of wait

waitForSelector() and a locator click solve related but different problems. waitForSelector() waits for a matching element to be added to the DOM. With { visible: true }, it also requires that the element not have display: none or visibility: hidden. Those checks do not include all locator click preconditions, such as enabled state, viewport placement, and a stable bounding box.

await page.waitForSelector('button.submit', { visible: true });
await page.locator('button.submit').click();

This can be useful when you need to wait for presence or the documented visibility condition before doing other work. It is not a substitute for the locator’s action-readiness checks. Puppeteer documents this method as working across navigations; see Page.waitForSelector.

Locator versus lower-level element workflows

Approach What it helps with Trade-off
page.locator(selector).click() Waits for documented action conditions, including visibility, enabled state, viewport placement, and bounding-box stability. Use a precise selector and verify the application outcome separately.
waitForSelector() followed by a click Waits for DOM presence and, optionally, the API’s visibility condition. Presence or visibility alone does not establish every click precondition.
ElementHandle or lower-level interaction Offers a more explicit workflow when the script needs to manage a particular element handle. The script has more responsibility for current element state and readiness; avoid retaining a handle across changes that can replace the element.

Locator methods also provide controls such as per-locator timeouts, waiting for enabled state, waiting for a stable bounding box, and filtering. Use the Locator API documentation for the installed version’s exact interfaces.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Verify the result, not just the click

  1. Use the intended target. Make the selector specific enough to identify the control you mean, especially if the page repeats buttons or includes hidden copies.
  2. Wait through the click. Use the locator API and note whether it succeeds or times out. A timeout points to a readiness or targeting issue to investigate; it does not by itself explain the page’s behavior.
  3. Coordinate navigation when applicable. Register waitForNavigation() alongside the click if the expected action navigates or reloads.
  4. Assert an application outcome. Wait for a page-specific success condition instead of assuming a resolved click means the task completed.
  5. Record the failing run. Capture the selector, Puppeteer and browser versions, frame, relevant element state, error or timeout, and whether the action should navigate. Compare these details between successful and failing runs.

Troubleshoot the common failure patterns

The click times out before it happens

  • Possible issue: The selector does not match the intended element, the target appears late, remains hidden or disabled, or does not become stable.
  • What to do: Confirm the selector against the failing page, inspect the target’s state, and use the locator’s timeout controls only after checking the installed version’s API. Increasing a timeout can accommodate a genuinely slow page; it will not fix a wrong selector or a control that never becomes actionable.

The click resolves, but nothing appears to happen

  • Possible issue: The click reached an element, but the application did not produce the expected outcome, or the script is checking too soon.
  • What to do: Wait for the specific state that should follow the interaction, such as a success message or changed content. Do not infer success from the click promise alone.

The run hangs or times out after a navigation click

  • Possible issue: The navigation wait started after the click, the action does not produce a navigation Puppeteer recognizes, or the chosen navigation condition does not match the page.
  • What to do: Register the wait and click in one Promise.all. If the action is an in-page update rather than a navigation, wait for that page-specific result instead.

The target appears present but is not ready

  • Possible issue: A DOM-presence check was mistaken for proof that the element is visible, enabled, in view, and stable.
  • What to do: Use a locator click for its broader action checks. waitForSelector(selector, { visible: true }) only adds the documented visibility condition to the presence wait.

A browser warning page interrupts navigation

Puppeteer’s troubleshooting documentation describes a specific Chrome-for-Testing case in which remote HTTP navigation can be blocked by a Chrome warning page with a continuation button that Puppeteer can click. Treat this as a possibility only when that warning page is actually the symptom, not as the default explanation for intermittent clicks. See Puppeteer troubleshooting.

Or skip the browser setup

If your goal is to save a page as an image or PDF rather than automate an interactive click, ScreenshotNeo offers a website screenshot API and MCP server. A single GET request can return a PNG, JPEG, WebP, or PDF. Install no browser automation for this call; create an API key and use:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for options and response details. Cookie banners and consent interfaces, newsletter popups, and chat widgets are removed before capture; those cleanup steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, with response headers reporting the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.

Sign up free for 1,000 screenshots a month, with no card required.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

  • Avoid arbitrary sleeps as a first fix. A fixed delay may waste time on fast runs and still be too short on slow ones. Prefer waiting for the interaction’s readiness or the actual page outcome.
  • Keep waits specific. Waiting for navigation is appropriate only when a recognized navigation is expected; a page-specific condition is better for in-place updates.
  • Use retries carefully. Repeating a click can submit a form twice or trigger an action more than once. Establish whether the action is safe to repeat before adding retries.
  • Do not infer a failure rate. Puppeteer’s documentation describes the APIs and conditions, but does not quantify how often clicks fail or how much locators reduce failures.

Frequently Asked Questions

Does `waitForSelector(selector, { visible: true })` make a click reliable by itself?

No. It checks DOM presence and the documented visibility condition, but not every locator click condition, including enabled state, viewport placement, and a stable bounding box.

Should I wait for navigation after every Puppeteer click?

No. Coordinate a navigation wait only when the action is expected to navigate or reload. For in-page updates, wait for the page-specific result instead.

Can the click promise resolve even though my automation did not accomplish its goal?

Yes. A resolved click indicates the click action completed, not that the application accepted it or reached the state your script intended.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.