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.

Use Puppeteer’s Frame object to work with an iframe. Enumerate attached frames with page.frames() or the page.mainFrame().childFrames() tree. If you already have the iframe element, call contentFrame(). Then use selectors or, preferably, frame-scoped locators such as frame.locator(...) to fill fields, click controls and wait for content. A selector run on page cannot see elements inside a child frame.

The iframe mental model

An iframe contains a separate document and JavaScript context. Puppeteer exposes that document as a Frame, while the outer page is the main frame. You must first obtain the correct frame, then run queries and actions through it.

  • Main frame: page.mainFrame(), containing the top-level document.
  • Attached frames: page.frames(), including the main frame and all descendants.
  • Child frames: frame.childFrames(), useful for walking nested iframe trees.
  • Element-to-frame bridge: ElementHandle.contentFrame(), which converts an iframe element handle into its associated Frame.

Frame URLs, names and the owning iframe’s attributes are useful identifiers. Avoid assuming that the first frame in an array is the one you need: advertising, analytics and payment providers commonly add several frames.

Puppeteer documentation pages currently show 25.9.0 through 25.12.0 in different places. Check the API reference for the version installed in your project before relying on a newly introduced method or option.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

Find an already attached iframe

Inspect every frame

const frames = page.frames();
for (const frame of frames) {
  console.log({
    url: frame.url(),
    name: frame.name(),
    isMain: frame === page.mainFrame()
  });
}

This is the fastest diagnostic when you do not yet know how a site identifies its iframe. Look for a stable URL path, a meaningful name, or a frame whose owning element has a distinctive ID.

Traverse the frame tree

function printFrameTree(frame, depth = 0) {
  console.log(`${'  '.repeat(depth)}${frame.name() || '(unnamed)'} ${frame.url()}`);
  for (const child of frame.childFrames()) {
    printFrameTree(child, depth + 1);
  }
}

printFrameTree(page.mainFrame());

The tree makes nesting visible. A frame’s children are not automatically searched by code running in its parent, so retain the specific Frame you intend to use.

Match a frame by URL or name

const paymentFrame = page.frames().find(frame =>
  frame.url().includes('/checkout/payment') || frame.name() === 'payment'
);
if (!paymentFrame) throw new Error('Payment frame is not attached');

await paymentFrame.locator('input[name="cardnumber"]').fill('4111111111111111');

URL matching should tolerate query strings and versioned paths. If a provider changes URLs frequently, identify the iframe element by a stable attribute and use contentFrame() instead.

Get a Frame from an iframe element

When you know the iframe’s selector, wait for the element and convert it:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const iframeElement = await page.waitForSelector('iframe#payment');
if (!iframeElement) throw new Error('iframe not found');

const frame = await iframeElement.contentFrame();
if (!frame) throw new Error('iframe has no accessible frame');

await frame.locator('input[name="email"]').fill('reader@example.test');
await frame.locator('button[type="submit"]').click();

contentFrame() can return null when the handle is not an iframe, the frame has not attached, or it has been detached. Treat that result as a lifecycle condition rather than dereferencing it blindly.

Interact with controls inside the frame

Prefer frame-scoped locators

Locators perform readiness checks and retry actions when a target is not yet actionable. They also make the frame boundary explicit:

await frame.locator('input[name="email"]').fill('reader@example.test');
await frame.locator('select[name="country"]').select('US');
await frame.locator('button[type="submit"]').click();

Adapt selectors and values to the target page. A selector such as button[type="submit"] is only an example; prefer accessible roles, labels or stable data attributes when the site provides them.

Rank #2
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.

Use lower-level selectors when you need a handle

const submit = await frame.$('button[type="submit"]');
if (!submit) throw new Error('Submit control is missing');
await submit.click();
await submit.dispose();

Dispose of retained element handles when finished, especially in long-running workers. For ordinary interactions, locators are the recommended default.

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

Read text or properties

const status = await frame.locator('[role="status"]').textContent();
const value = await frame.locator('input[name="email"]').inputValue();
console.log({ status, value });

These calls execute in the child frame’s context. Do not pass a node from the main document to a frame method or vice versa.

Wait for a frame that is inserted later

For dynamically created iframes, wait for attachment instead of sleeping for an arbitrary number of milliseconds:

const frame = await page.waitForFrame(async candidate => {
  const element = await candidate.frameElement();
  if (!element) return false;
  return await element.evaluate(el => el.id === 'payment');
});

await frame.locator('button[type="submit"]').click();

The predicate can inspect the frame URL, name or owning element. A URL form is useful when the destination is stable:

const frame = await page.waitForFrame(candidate =>
  candidate.url().startsWith('https://payments.example.test/embedded')
);

If your installed Puppeteer release exposes a different overload, follow that release’s API signature; the essential behavior is to await frame attachment rather than use a fixed delay.

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.

Wait for content after the frame attaches

Attachment does not guarantee that the inner document has rendered its controls. Wait inside the frame:

await frame.waitForSelector('input[name="cardnumber"]');
await frame.locator('button[type="submit"]').wait();
await frame.locator('input[name="cardnumber"]').fill('4111111111111111');

Frame.waitForSelector() is designed to continue across navigations and throws when the selector never appears. A locator’s wait() is convenient when the same locator will immediately be used for an action. Prefer a condition tied to the element you need over page.waitForTimeout().

Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

Handle nested iframes

Resolve each level in sequence. The outer frame must be used to find the inner iframe:

const outerElement = await page.waitForSelector('iframe#outer');
const outer = await outerElement.contentFrame();
if (!outer) throw new Error('Outer frame unavailable');

const innerElement = await outer.waitForSelector('iframe#inner');
const inner = await innerElement.contentFrame();
if (!inner) throw new Error('Inner frame unavailable');

await inner.locator('button[data-action="confirm"]').click();

Alternatively, inspect outer.childFrames() and match the nested frame by URL or name. JavaScript executed in outer cannot automatically reach inner.

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

Frame lifecycle: navigation and detachment

Frames can attach, navigate, and detach while your script is running. A handle or locator associated with an old document may no longer be valid after navigation. Reacquire the frame and target after a meaningful page transition:

await frame.locator('button[data-next]').click();

const refreshed = await page.waitForFrame(candidate =>
  candidate.url().includes('/step-two')
);
await refreshed.locator('input[name="verification"]').fill('123456');

When an action reports that the frame was detached, stop using the old reference, locate the newly attached frame, and repeat the wait for its inner control.

Why common approaches fail

page.$() cannot find an inner element

page.$() searches only the main document. Obtain the iframe’s Frame and call frame.$(), frame.waitForSelector() or frame.locator().

The iframe selector matches, but contentFrame() is null

The element may not have attached its browsing context yet, may have been detached, or may not be a genuine iframe element. Re-query it after the page transition and verify the element selector.

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

waitForFrame() times out

Check the frame list and log URLs. The site may create the iframe only after a click, use a different URL, or block the request. Replace a brittle exact URL with a stable predicate, and trigger the UI action before waiting if creation is user-driven.

Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft

The frame exists but its control never appears

The inner application may still be loading, may have navigated, or may render a different variant for your locale or user agent. Wait for a selector that is guaranteed for that state, inspect frame.url(), and increase the operation timeout only after confirming the page is genuinely slow.

A nested control is inaccessible

Walk into the parent frame first, then resolve the nested iframe. Do not try to cross two frame boundaries with one selector.

An action fails intermittently

Use locators so Puppeteer can wait for visibility and actionability. If the frame navigates during the action, reacquire the frame and locator after navigation rather than retrying a stale handle.

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

A reusable helper

This helper accepts an iframe selector and a callback, centralizing null checks:

async function withFrame(page, iframeSelector, work) {
  const element = await page.waitForSelector(iframeSelector);
  if (!element) throw new Error(`Missing iframe: ${iframeSelector}`);

  const frame = await element.contentFrame();
  if (!frame) throw new Error(`No browsing context: ${iframeSelector}`);

  return work(frame);
}

await withFrame(page, 'iframe#payment', async frame => {
  await frame.locator('input[name="email"]').fill('reader@example.test');
  await frame.locator('button[type="submit"]').click();
});

For an asynchronously inserted iframe, replace the initial selector wait with page.waitForFrame(), then apply the same callback pattern.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability and security notes

  • Log frame name and URL during diagnosis, but avoid logging payment data, tokens or personally identifiable information.
  • Use stable predicates and element conditions instead of long fixed sleeps; this reduces idle time and makes failures deterministic.
  • Set practical navigation and selector timeouts for your workload, and capture the frame URL and last successful step in errors.
  • Expect third-party frames to change markup independently of your application. Encapsulate selectors and keep a fallback identification strategy.
  • Cross-origin frames are still addressable through Puppeteer’s Frame API; browser same-origin rules do not make the frame’s document part of the parent, so always operate through the frame object.

Or skip the browser setup

If your goal is a clean image or PDF rather than interactive form automation, ScreenshotNeo provides a website screenshot API and MCP server. A single request captures a URL:

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 all parameters and response headers. The same request in Python:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

And in Node.js:

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 failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Before capture, ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server supplies take_screenshot, get_page_info and capture_pdf tools to Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up for the free plan.

FAQ

Can Puppeteer click an iframe’s button without switching frames?

No. Resolve the iframe to a Frame first, then click through that frame’s locator or selector API.

Should I use a frame URL or an iframe ID?

Use whichever is more stable on the target site. An iframe ID is usually preferable when the provider’s URL contains changing query parameters; URL matching is useful when the element attributes are generated.

Does a frame locator survive a full iframe navigation?

Locators are safer than retained element handles, but after a frame is replaced or detached you should reacquire the current frame and target explicitly.

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

Frequently Asked Questions

What is the difference between page.frames() and childFrames()?

page.frames() returns all attached frames, including the main frame. childFrames() returns only the immediate descendants of a particular Frame, allowing you to traverse a nested tree.

Why does contentFrame() return null after a page update?

The iframe element handle may refer to a detached element or a browsing context that has not attached. Query the current iframe again, then call contentFrame() and check the result.

Is waitForTimeout a reliable way to wait for an iframe?

No. Wait for the frame with page.waitForFrame() and wait for a required inner selector with a frame locator or Frame.waitForSelector(). These conditions track actual readiness.

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.