How to Hover Over Elements With Puppeteer Locators
Use Puppeteer’s locator API to hover reliably, handle menus and timeouts, and troubleshoot common issues with runnable examples.
To hover over an element with a Puppeteer locator, create a locator for the target and await hover():
await page.locator('.menu-item').hover();
Use a selector that uniquely identifies the element. Locator actions wait for the target to become actionable and retry when readiness conditions are not yet met. If hovering opens a menu or triggers an animation or request, wait separately for the resulting state.
1. Hover with a locator
This complete Node.js example launches Chromium, opens a page, hovers a target, and closes the browser. Install Puppeteer 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.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('a').hover();
// Add an assertion or wait here if the hover should reveal UI.
} finally {
await browser.close();
}
})();
Replace 'a' with a selector for the element you intend to hover. A broad selector may match the wrong element; prefer a class, ID, or other specific selector. Puppeteer recommends locators for selecting and interacting with elements. See the official page interactions guide and Locator.hover() API reference.
Wait for the hover result
hover() performs the pointer action; it does not guarantee that application-specific animations, state updates, or network work have finished. Wait for the visible result after hovering:
await page.locator('.menu-item').hover();
await page.locator('.submenu').wait();
For a test, use the assertion library already in your project to check the expected menu state. Keep the target locator and the result locator distinct when they represent different elements.
2. Choose a selector and timeout
CSS selectors work directly, and Puppeteer supports selector syntax for text, accessibility attributes, XPath, and shadow DOM. Choose a selector that expresses the target unambiguously; see the Page.locator() reference for the selector parameter.
Locators use the page timeout by default. Set a timeout on an individual locator when a particular target needs more time:
await page
.locator('.menu-item')
.setTimeout(3000)
.hover();
The timeout is in milliseconds. Increase it only when the page legitimately needs more time to render or satisfy action preconditions. A longer timeout cannot fix a selector that never matches.
3. What locator hover waits for
Before acting, Puppeteer checks that the element is in the viewport, waits for visibility as needed, and waits for a stable bounding box across two consecutive animation frames. Locator actions retry while the target is not ready, subject to the configured timeout. These checks reduce timing problems caused by ordinary rendering changes; they do not replace waiting for the application state caused by the hover. Details are in the Locator class reference and the interactions guide.
4. Page-level alternative
Puppeteer also documents page.hover(selector). It is a page-level selector API: it scrolls the target into view if needed and moves the pointer to its center. If multiple elements match, it uses the first; if none match, it throws. For new code, the locator form makes the target and its action explicit.
await page.hover('.menu-item');
Use the locator form when you want locator action readiness and per-locator timeout configuration. See the official Page.hover() reference for the page-level behavior.
5. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Locator hover times out | The selector does not match, the element is not visible, or action preconditions are not met before the timeout. | Check the selector and page state. If the element loads asynchronously, wait for it or set an appropriate locator timeout. |
| The wrong element reacts | The selector is broad or matches multiple elements. | Use a more specific selector that identifies the intended target. |
| Hover succeeds but the menu is missing | The pointer action completed, but the application’s animation or state update has not completed. | Wait for the menu or other expected state after calling hover(). |
page.hover() acts on an unexpected match |
Its documented behavior uses the first matching element. | Refine the selector or use a locator targeted to the intended element. |
6. Reliability and performance notes
- Keep selectors specific. This makes the intended target clear and helps avoid first-match surprises in page-level hover.
- Use readiness checks instead of arbitrary delays where possible. A locator waits for actionability conditions; wait for a specific resulting UI state after the action.
- Set realistic timeouts. A timeout should allow for expected page rendering without hiding a broken selector or missing state.
- Close the browser in a
finallyblock. This ensures Chromium is shut down if navigation or interaction fails.
Hover is a browser automation action, so there is no physical mouse or additional hardware requirement. Its cost and runtime depend on your own browser execution environment; the cited Puppeteer documentation does not provide a universal benchmark.
7. Or skip the browser setup
If your goal is to capture a page rather than automate a hover interaction, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call API returns a screenshot or PDF; it does not perform this Puppeteer hover action.
See the ScreenshotNeo API documentation for options and setup.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = require('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
- Cookie banners, popups, and chat widgets are removed before the shot.
- Bot checks, blank pages, and failed loads are never billed.
- An MCP server lets AI agents take screenshots.
- 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
8. FAQ
Does locator hover move to the element’s center?
The official locator reference describes hovering over the located element but does not specify a pointer coordinate in the cited material. The page-level page.hover() documentation explicitly says it moves to the center.
Can I hover an element inside a shadow root?
Puppeteer supports selector syntax for shadow DOM. Use a selector that resolves to the intended element, then call hover() on its locator.
Should I use a fixed delay after hover?
Prefer waiting for the actual result, such as a submenu becoming visible. A fixed delay can be too short on a slow page and unnecessarily long on a fast one.


