How to Right-Click an Element with Puppeteer in JavaScript
Right-click any element in Puppeteer with button: 'right'. Learn locator and selector patterns, context menus, frames, troubleshooting, and reliable automation.
Use Puppeteer’s click options and set button to 'right':
await page.locator('#target').click({ button: 'right' });
The selector-based equivalent is:
await page.click('#target', { button: 'right' });
'right' is the explicit right mouse-button value. Puppeteer’s mouse button defaults to 'left', so omitting the option performs a normal left click instead. Puppeteer recommends locators for selecting and interacting with elements because they check conditions such as visibility, viewport placement, enabled state, and a stable bounding box before acting.
1. Set up a complete Puppeteer script
Install Puppeteer and launch a browser, then perform the right-click inside an asynchronous function:
npm install puppeteer
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('h1').click({ button: 'right' });
await browser.close();
})();
Use a selector that identifies the element you actually want to target. CSS selectors are supported by default, and Puppeteer also supports its documented selector syntax.
2. Right-click with a locator (recommended)
The locator API is the clearest option for new code:
await page.locator('[data-testid="file-row"]').click({ button: 'right' });
Locators retry while the element is not ready and perform actionability checks before clicking. This helps when a page renders the target after navigation or when layout changes move the element.
Use a text or role locator
await page.getByText('Quarterly report').click({ button: 'right' });
await page.getByRole('button', { name: 'More actions' }).click({ button: 'right' });
Choose a selector that remains stable when classes or layout details change. A dedicated ID, test attribute, accessible role, or visible name is usually more durable than a generated class name.
3. Use page.click when you need the selector form
page.click(selector, options) remains a documented alternative and is useful in compact scripts or older codebases:
await page.click('#target', { button: 'right' });
Puppeteer finds the matching element, scrolls it into view when needed, and clicks its center. If no element matches, the promise rejects, so handle navigation and selector timing before this call.
4. Capture a page’s context-menu response
A right-button action may cause the page or browser to display a context menu. Puppeteer sends synthetic mouse input; it does not promise that every application will respond exactly like a physical mouse. If the page has a custom menu, wait for the menu’s selector after the click:
await page.locator('#target').click({ button: 'right' });
await page.locator('[role="menu"]').wait();
await page.getByRole('menuitem', { name: 'Rename' }).click();
If the site suppresses the native menu with a contextmenu handler, interact with the application’s resulting DOM instead of expecting an operating-system menu.
5. Make the target reliable before clicking
Wait for navigation first
await page.goto('https://example.com/dashboard', {
waitUntil: 'networkidle2'
});
await page.locator('#target').click({ button: 'right' });
For applications that keep long-lived connections open, wait for a specific element rather than global network idleness:
await page.goto('https://example.com/dashboard');
await page.locator('#target').wait();
await page.locator('#target').click({ button: 'right' });
Wait for a menu or state change
await page.locator('#target').click({ button: 'right' });
const menu = page.locator('#context-menu');
await menu.wait();
await menu.getByText('Delete').click();
Scroll and inspect the target
Locators and page.click handle normal scrolling into view. When debugging, inspect the element and its bounding box:
const target = page.locator('#target');
console.log(await target.evaluate(el => ({
tag: el.tagName,
text: el.textContent,
rect: el.getBoundingClientRect().toJSON()
})));
await target.click({ button: 'right' });
6. Right-click elements inside frames
The target must be selected from the frame that owns it. A selector on the top-level page cannot reach an element inside an iframe.
const frame = page.frames().find(f => f.url().includes('/embedded-app'));
if (!frame) throw new Error('Embedded frame was not found');
await frame.locator('#target').click({ button: 'right' });
Wait for the frame or its target content to load before interacting. If the iframe is cross-origin, use the frame object rather than trying to query it from the parent document.
7. Shadow DOM and dynamic targets
For a shadow-root target, use Puppeteer’s supported selector features or locate the host and query within the component’s DOM. The important requirement remains the same: apply { button: 'right' } to the locator that resolves to the clickable element.
await page.locator('my-file-card').locator('#target').click({ button: 'right' });
For lists that re-render after each action, resolve a fresh locator before every right-click instead of retaining assumptions about an old element handle.
8. Complete example: custom context menu
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://example.com/files', {
waitUntil: 'domcontentloaded'
});
const file = page.locator('[data-file-id="report-2026"]');
await file.click({ button: 'right' });
const menu = page.locator('[role="menu"]');
await menu.wait();
await menu.getByRole('menuitem', { name: 'Download' }).click();
await browser.close();
})();
9. Troubleshooting
| Error or symptom | Cause | Fix |
|---|---|---|
TargetCloseError or a closed browser |
The browser or page closed before the asynchronous click completed. | Keep the browser open until the click and any resulting action finish; close it in a final cleanup step. |
| Selector does not match | The selector is wrong, the page has not rendered the element, or the element is inside an iframe. | Verify the selector in the page, wait for the target, and select it through the owning frame. |
| Click is rejected because the element is not actionable | The element is hidden, disabled, moving, or outside the usable viewport. | Wait for the target, remove the state that blocks interaction, and use a locator so Puppeteer can retry its preconditions. |
| No visible context menu | The application prevents the native menu or handles contextmenu itself. |
Assert the application’s custom menu or state change rather than a browser menu. |
| Right-click appears to do nothing | The target’s event handler is attached elsewhere, or synthetic events are not equivalent to a physical mouse interaction. | Inspect the event path and test the page’s supported interaction. Puppeteer documents that its mouse events do not fully reproduce normal physical mouse functionality. |
| The wrong item is clicked | A broad selector matches multiple elements or a list changed between locating and clicking. | Use a unique selector, scope the locator to its row or container, and resolve it immediately before the action. |
10. Performance, reliability, and cost considerations
- Reuse one browser process and create pages as needed instead of launching a new browser for every click.
- Prefer a specific readiness condition for the target over an arbitrary sleep. This reduces idle time and makes failures easier to diagnose.
- Use stable selectors and assert the expected menu or state after the click so a silent UI change does not pass unnoticed.
- Close pages and browsers in cleanup code, especially in workers processing many URLs.
- Right-click itself has no special external cost; runtime and infrastructure costs come from launching browsers, loading pages, and any downstream work.
11. Or skip the browser setup
If your goal is to obtain a clean screenshot after preparing a page, ScreenshotNeo provides a website screenshot API. Its one-call endpoint can capture a URL without maintaining Puppeteer infrastructure:
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}`);
See the ScreenshotNeo documentation for request options. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
12. FAQ
What is the exact right-click option?
Pass { button: 'right' } to the locator or page click call.
Can I right-click with a CSS selector?
Yes. For example: await page.click('.file-row', { button: 'right' }).
Does Puppeteer open a native context menu every time?
No. The page may suppress the native menu or show its own menu, and Puppeteer notes that synthetic mouse events do not fully reproduce physical mouse behavior.
Should new code use locators?
Yes. Puppeteer’s page-interactions guide recommends locators for selecting and interacting with elements.


