For a Puppeteer action such as clicking or hovering, use a locator directly: locator readiness waits for a stable bounding box across two consecutive animation frames. If you need to wait for geometry on its own, or need a different condition, use page.waitForFunction() with animation-frame polling and compare successive bounding boxes.
Choose the wait that matches the job
| Need | Use | What it checks |
|---|---|---|
| Perform a supported interaction once the element is ready | A locator action such as click(), fill(), or hover() |
Puppeteer’s locator readiness includes a stable bounding box over two consecutive animation frames. |
| Wait for geometry without immediately acting, or require a custom rule | page.waitForFunction() |
Your predicate can compare position alone or the full bounding box and define its own tolerance and sample count. |
| Wait for an element to appear or become visible | page.waitForSelector() |
Selector presence or visibility; this does not establish geometric stability. |
The documented two-frame locator check is a readiness check, not a guarantee that the page can never move the element later. Avoid adding a fixed sleep before an action when the locator already supplies the relevant readiness behavior.
As an Amazon Associate I earn from qualifying purchases.
Use locator readiness for an action
When the next step is a locator action, call it directly rather than performing a separate geometry wait:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →const target = page.locator('.target');
await target.click();
Puppeteer’s Page interactions guide describes the stable bounding-box check as part of locator action readiness. This is the simplest option when its built-in condition matches your need.
#1 Best Overall
Wait for a stable bounding box yourself
Use waitForFunction() when stability itself is the result you need. The function runs in the page context, and Puppeteer resolves the wait when it returns a truthy value. With polling: 'raf', it evaluates on animation frames, which is useful for changes driven by styling or animation.
await page.waitForFunction(
selector => {
const element = document.querySelector(selector);
if (!element) return false;
const rect = element.getBoundingClientRect();
const current = [rect.x, rect.y, rect.width, rect.height];
const previous = window.__previousRect;
window.__previousRect = current;
if (!previous) return false;
return current.every((value, index) => Math.abs(value - previous[index]) < 0.5);
},
{ polling: 'raf', timeout: 10_000 },
'.target',
);
This example compares all four bounding-box values with a tolerance of less than 0.5 CSS pixels between consecutive samples. That tolerance and the two-sample pattern are implementation choices, not a Puppeteer-prescribed threshold. The API’s documented options and argument passing are described in the Page.waitForFunction API reference.
Rank #2
Choose which geometry matters
- For position only, compare
xandy; ignore width and height. - For a settled layout box, compare
x,y,width, andheight. - Choose a tolerance suitable for the coordinate precision your page requires. A very small tolerance can keep the wait from resolving if values continue to fluctuate.
Handle missing or replaced elements
The sample returns false when the selector is absent. If the element disappears and is later replaced, the next available measurement starts a new comparison against the most recently stored sample. For stricter behavior, explicitly reset the prior sample when the element is absent or track element identity so that two different DOM nodes are not treated as one continuously stable element.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →The example stores scratch state on window for clarity. In production, avoid a generic global property that could collide with page code; use uniquely scoped state or an explicit evaluation/observer design appropriate to your page.
Use selector waits for presence, not position stability
waitForSelector() is appropriate when the requirement is that a matching element appears, optionally with visibility conditions. It can work across navigations and throws if the selector does not appear before its timeout. It does not check whether the element’s bounding box has stopped changing. See the Page.waitForSelector API reference.
Timeouts, polling, and failure handling
The Puppeteer 25.12.0 API documentation lists a 30-second default timeout for waitForFunction(); it can be configured per call or through Page.setDefaultTimeout(). The options also support abort signals. Check the documentation for the version installed in your project before relying on version-specific defaults: WaitForFunctionOptions reference.
Rank #4
- Timeout: Treat a timeout as an expected failure path. Check that the selector matches the intended element, that it appears before the deadline, and that the measured geometry can settle under your predicate.
- Wrong polling model: Use
rafwhen you need frame-by-frame observation of visual changes. Mutation polling is a separate option and is better suited to DOM mutations; it does not itself prove stable geometry. - Element replacement: Decide whether replacement should restart sampling. If it should, compare the node identity as well as its rectangle.
- Overly strict comparison: If fractional coordinates fluctuate, adjust the tolerance to the precision your task actually requires rather than extending the timeout indefinitely.
- Presence mistaken for stability: A successful selector wait establishes appearance or visibility, not a stationary bounding box; follow it with a geometry predicate if that is the real condition.
Or skip the browser setup
If your goal is a website screenshot rather than controlling Puppeteer in your own browser, ScreenshotNeo provides a screenshot API and MCP server. A single GET request can return an image or PDF. For example, using cURL:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutecurl -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 API details. It accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; these steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. 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 required.
Frequently Asked Questions
Does waitForSelector() wait until an element stops moving?
No. It waits for a matching element to appear, with optional visibility checks; use a geometry predicate for position stability.
Best Value
- Used Book in Good Condition
Does Puppeteer’s two-frame stability check guarantee the element will never move again?
No. It is a short locator-readiness check across two consecutive animation frames, not a guarantee against later page changes.
Quick Recap
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.
Recommended Free Tools




