ScreenshotNeo

BlogHow-to

How to Convert a Puppeteer ElementHandle to a Locator

Convert an existing Puppeteer ElementHandle with asLocator(). Learn what the conversion preserves, when to use a selector-backed locator, and how to handle stale elements.

By the ScreenshotNeo team4 October 20265 min read

Call asLocator() on the existing Puppeteer ElementHandle:

const locator = elementHandle.asLocator();

This is synchronous: do not write await. The result is a Locator<Element> backed by that particular handle. It lets you use locator action preconditions, but it does not look the element up again if the handle becomes stale. If you need Puppeteer to resolve an element from a selector when an action runs, create a selector-backed locator with page.locator() or frame.locator() instead. See the ElementHandle.asLocator() reference and Puppeteer’s page interaction guide.

Convert an existing handle

Use asLocator() when you already found the exact element and want to perform a locator action on that handle:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });

  const buttonHandle = await page.waitForSelector('button.submit');
  if (!buttonHandle) {
    throw new Error('Submit button was not found');
  }

  // asLocator() is synchronous. It wraps this existing handle.
  const buttonLocator = buttonHandle.asLocator();
  await buttonLocator.click();
} finally {
  await browser.close();
}

The null check matters for selector APIs and Puppeteer versions whose types or options permit a null result. Check the API for the version installed in your project. The documented signature for asLocator() is asLocator(this: ElementHandle<Element>): Locator<Element>.

TypeScript example with a helper

import type { ElementHandle, Locator } from 'puppeteer';

function locatorFromExistingHandle(handle: ElementHandle<Element>): Locator<Element> {
  return handle.asLocator();
}

const handle = await page.waitForSelector('button.submit');
if (!handle) throw new Error('Submit button was not found');

const locator = locatorFromExistingHandle(handle);
await locator.click();

If your project has a type mismatch, confirm the installed Puppeteer version and import types from the package your runtime uses. The API reference documents an element locator return type; selector-based APIs may infer a more specific element type from the selector.

Choose handle-backed or selector-backed lookup

Approach How it identifies the element Use it when Staleness behavior
handle.asLocator() The specific DOM element represented by the existing handle You already have the intended element and want locator preconditions for an action It cannot refresh a stale handle
page.locator(selector) A selector strategy resolved for the action You want the element located from a selector when the action runs It can retry the operation when the object is not ready, according to locator behavior
frame.locator(selector) A selector strategy scoped to a frame The target belongs to a particular frame It uses the frame’s selector lookup strategy

For new code where the action should target whichever matching element is present at action time, prefer a locator built directly from the selector:

await page.locator('button.submit').click();

// For content inside a particular frame:
const frame = page.frames().find((candidate) => candidate.url().includes('/checkout'));
if (!frame) throw new Error('Checkout frame was not found');
await frame.locator('button.submit').click();

Puppeteer recommends locators for selecting and interacting with elements. Locator actions wait for relevant readiness conditions; the interaction guide describes checks such as viewport presence, visibility, enabled state, and a stable bounding box for clicking. Use the handle form when the handle itself is meaningful; use a selector-backed locator when fresh resolution is important.

What the conversion does and does not do

  • It wraps the existing element reference. The method does not convert the handle into a selector or re-query the document.
  • It provides Locator behavior. You can use locator actions and their preconditions on that existing reference.
  • It cannot recover a stale handle. If navigation or DOM replacement invalidates the referenced element, calling asLocator() does not find a replacement.
  • It does not make selection fresh. If the page can replace the target between discovery and action, use a selector-backed locator for the action.

Puppeteer’s locator API includes methods such as click, fill, hover, scroll, wait, and waitHandle. Consult the installed version’s API for the exact methods available.

Common errors and fixes

Symptom Likely cause Fix
asLocator is not a function The value is not an ElementHandle, or the installed Puppeteer version does not expose the method. Check the value’s origin and inspect the API/types for the installed package version. Do not assume another browser automation library has the same method.
TypeScript says the handle may be null The selector call can return null under its API contract or options. Check for null before calling asLocator(), or use a lookup API whose contract fits your flow.
The action fails after a rerender or navigation The handle refers to a specific element that was detached or invalidated. Find the element again and create a new handle, or use page.locator(selector) so the selector strategy is used for the action.
The click times out or does not happen The element may not meet click preconditions, such as visibility, enabled state, viewport presence, or a stable bounding box. Check the page state and the locator action’s documented conditions. Wait for the application state that makes the control actionable; use a selector-backed locator if the element is replaced while waiting.
The locator targets the wrong repeated item The original handle came from a broad or ambiguous selection. Make the initial selection specific, or scope a selector-backed locator to a stable parent or frame.

Performance, reliability, and cost

asLocator() is a synchronous wrapper operation; the meaningful work is the later browser interaction. Choose based on correctness: a handle-backed locator preserves the element you already selected, while a selector-backed locator expresses how to find an element for an action. Neither choice removes the need to account for page navigation, DOM replacement, and action readiness.

For reliable automation, keep the lookup and action close when the target may be replaced, use a selector that identifies the intended element, and handle missing elements explicitly. Puppeteer’s cited documentation does not provide performance benchmarks or a cost figure for this conversion, so those should not be inferred from the API method alone.

Or skip the browser setup

If the task is to capture a page rather than interact with its controls, ScreenshotNeo provides a website screenshot API and MCP server. A single GET request returns a PNG, JPEG, WebP, or PDF. The API can accept cookie banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. AI agents can use its MCP tools, including take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API documentation for request options. This runnable cURL example saves a WebP screenshot:

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

Free includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; yearly billing gives two months free, and every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Can I convert an ElementHandle to a Locator?

Yes. Call elementHandle.asLocator() and use the returned locator.

Does asLocator() re-query the element?

No. It is based on the existing handle and cannot refresh it if stale.

Should I add await to asLocator()?

No. The conversion is synchronous. Await the locator action, such as click().

When should I use page.locator()?

Use it when you want Puppeteer to locate an element from a selector for the action, especially when the page may replace the DOM element.