What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use Puppeteer’s Frame.content() method to retrieve a frame’s complete HTML as a string, including its DOCTYPE:
const html = await frame.content();
If you’re starting with an iframe element, first call contentFrame() on its element handle to get the associated frame.
Get the frame’s HTML with Frame.content()
Once you have the Puppeteer Frame object you want, call:
const html = await frame.content();
The returned value is a string containing the frame’s full HTML document, including its DOCTYPE. This is the direct method for reading a frame’s complete HTML.
#1 Best Overall
Get a frame from an iframe element
If you know the iframe’s selector, find its element handle, resolve the associated frame, and then read its contents:
const iframeHandle = await page.$('iframe#report');
if (!iframeHandle) {
throw new Error('iframe#report was not found');
}
const frame = await iframeHandle.contentFrame();
const html = await frame.content();
console.log(html);
ElementHandle.contentFrame() resolves the frame associated with the element. For an HTMLIFrameElement, that associated frame exists. Check that the selector matched before calling the method so a missing iframe is handled clearly.
Complete runnable example
Install Puppeteer in your project, then run this ES module example. Replace the URL and selector with the page and iframe you need to inspect:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
const iframeHandle = await page.$('iframe#report');
if (!iframeHandle) {
throw new Error('iframe#report was not found');
}
const frame = await iframeHandle.contentFrame();
const html = await frame.content();
console.log(html);
} finally {
await browser.close();
}
If the frame is populated dynamically, wait for a condition tied to the content you need before calling content(). There is no single readiness condition that suits every page; choose one based on how that particular frame loads.
Recommended Free Tools
Rank #3
Choose the right Puppeteer method
| Need | Method | What it returns |
|---|---|---|
| The complete HTML of a frame | frame.content() |
The frame’s full HTML string, including DOCTYPE. |
| A DOM expression evaluated inside a frame | frame.evaluate(() => document.documentElement.outerHTML) |
The result of that expression in the frame’s context; use it when you specifically need a DOM expression’s result. |
| The page’s full HTML | page.content() |
The page document’s HTML, not a substitute for selecting a child frame when its embedded content is the target. |
| One matching element inside a frame | frame.$eval(selector, fn) |
The result of applying a function to the first matching element; it throws if no element matches. |
Find the intended frame when a page has several
A page may expose multiple frames. Choose the frame that contains the content you want, then call content(). Puppeteer’s frame tree provides frame URLs and child frames, but the selector or matching rule must be chosen for the target page.
Troubleshooting
- The iframe handle is missing: Check the selector and confirm the iframe exists before calling
contentFrame(). The example throws a specific error whenpage.$()finds no match. - The HTML is empty or incomplete: If the frame fills in dynamically, wait for a page-specific condition tied to its content before reading it.
- You got the top-level document instead of the embedded HTML:
page.content()reads page HTML. Resolve and read the desired child frame instead. frame.$eval()throws: It requires a matching element inside that frame. Useframe.content()for the entire frame document, or verify the selector if you need one element.
Or skip the browser setup
If you need a screenshot or PDF rather than the frame’s HTML string, ScreenshotNeo can return one with a single request. For example, this cURL command saves a screenshot:
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 API documentation for request options. Cookie banners are accepted and removed before capture, along with known newsletter popups and chat widgets; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. An MCP server provides screenshot tools for AI agents, and the free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000.
Sign up for 1,000 free screenshots a month, with no card required.
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.

