Puppeteer setContent(): Wait for Page Content to Load
Learn what Puppeteer’s setContent() waits for, how to choose a readiness condition, and how to handle dynamic content and timeouts.
await page.setContent(html) assigns HTML markup to the page and, by default, waits for the page’s load event. Set waitUntil to 'domcontentloaded' if document parsing is the threshold you need, then wait separately for an application-specific element or state when the page renders content asynchronously. The current Puppeteer SetContentWaitForOptions type excludes 'networkidle0' and 'networkidle2'; use page.waitForNetworkIdle() separately if network quiet is actually required.
Runnable example
Install Puppeteer in a Node.js project, then save this as set-content.mjs and run it with node set-content.mjs. This example uses the default load threshold, then reads the resulting document markup.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setContent('<!doctype html><main><h1>Report</h1></main>');
console.log(await page.title());
console.log((await page.locator('h1').textContent())?.trim());
} finally {
await browser.close();
}
setContent() resolves with void; it does not return the HTML. For reading the page’s full HTML after setting it, use page.content(). It returns the contents, including the doctype.
Choose the right wait condition
| Condition | Use it when | What it establishes |
|---|---|---|
'load' (default) |
You want the page load event as the threshold. | The load lifecycle event fired. |
'domcontentloaded' |
Parsed document markup is enough to continue. | The DOMContentLoaded lifecycle event fired. |
| A locator or selector wait | Your next step depends on an element produced by client-side work. | The selected element meets the wait’s relevant conditions. |
page.waitForNetworkIdle() |
The next step requires network quiet. | Network activity meets the configured idle threshold. |
Lifecycle events and application readiness are different signals. A load event does not, by itself, prove that an application has finished every asynchronous task. Pick a condition that corresponds to the thing your next operation needs.
Wait for parsing, then for a specific element
Use a selector tied to a meaningful ready state in your own page. Puppeteer’s current interaction guide recommends locators for element interactions.
const html = `<!doctype html>
<main>
<div id="report" data-ready="false"></div>
<script>
setTimeout(() => {
const report = document.querySelector('#report');
report.textContent = 'Report ready';
report.dataset.ready = 'true';
}, 100);
</script>
</main>`;
await page.setContent(html, { waitUntil: 'domcontentloaded' });
await page.locator('#report[data-ready="true"]').wait();
console.log(await page.locator('#report').textContent());
Replace the example state with a reliable condition exposed by the page under automation. If you prefer the lower-level selector API, page.waitForSelector() is also documented by Puppeteer.
Wait for network quiet separately
The general lifecycle definitions describe networkidle0 as no more than zero network connections for at least 500 ms, and networkidle2 as no more than two for at least 500 ms. Those values are not accepted in the current setContent() waitUntil type. The separate network-idle method has configurable idle time and concurrency; its defaults are 500 ms and zero connections.
await page.setContent(html, { waitUntil: 'domcontentloaded' });
await page.waitForNetworkIdle({ idleTime: 500, concurrency: 0 });
Network quiet is not interchangeable with a semantic “ready” signal. Pages with polling or persistent connections may not become idle when expected, while a quiet network does not necessarily mean a specific component is ready.
Options, arrays, and timeouts
setContent(html, options) accepts SetContentWaitForOptions, which extends Puppeteer’s general wait options. The relevant controls are:
waitUntil: one supported lifecycle event or an array of events. With an array, every listed event must fire. The default is'load'. The current type excludes'networkidle0'and'networkidle2'.timeout: the general wait option documents a 30,000 ms default. Set it to0to disable the timeout, or set a finite value suited to the work.- Default timeout settings:
page.setDefaultTimeout()andpage.setDefaultNavigationTimeout()can change defaults.
// Both lifecycle events must fire.
await page.setContent(html, {
waitUntil: ['domcontentloaded', 'load'],
timeout: 45_000,
});
// Set a page-wide default for waits that use the default timeout.
page.setDefaultTimeout(20_000);
await page.setContent(html, { waitUntil: 'domcontentloaded' });
Use a longer timeout only when the expected work needs it. Disabling timeouts can make a stalled operation wait indefinitely.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
TypeScript rejects 'networkidle0' or 'networkidle2'. |
The current SetContentWaitForOptions type excludes these values. |
Use a supported waitUntil value, then call page.waitForNetworkIdle() separately if needed. |
| The next query finds no dynamically rendered element. | The chosen lifecycle threshold completed before the application-specific content appeared. | After setContent(), wait for a locator or selector that represents the required state. |
setContent() times out. |
The selected lifecycle event did not fire within the timeout budget. | Confirm that the markup and its resources can complete, choose the appropriate threshold, or adjust the timeout. Avoid setting it to zero unless an unbounded wait is intended. |
| A network-idle wait never resolves. | Network activity may continue, or its configured quiet period may not be reached. | Check whether network quiet is necessary; if so, set suitable idle and concurrency values. Otherwise wait for the page’s specific ready state. |
| The call completes but an image or other resource is not usable. | The selected lifecycle threshold may not match the resource readiness needed by the next step. | Wait for the relevant resource or page condition explicitly before using it. |
Reliability and performance notes
- Choose the earliest condition that safely supports the next operation. Waiting for extra lifecycle events or network quiet adds waiting and can make automation more sensitive to activity unrelated to the required result.
- Prefer a specific ready element or state when the task depends on application output. Make the condition deterministic and tied to the work being automated.
- Give each wait a finite, realistic timeout, and handle timeout errors in the surrounding automation so failures are visible and recoverable.
- When debugging, log which stage failed: content assignment, lifecycle wait, application condition, or subsequent interaction. This narrows down whether the issue is page readiness or later work.
Or skip the browser setup
If your goal is a screenshot rather than browser automation, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call API returns an image or PDF, with options for waits and other capture behavior. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up free and get 1,000 screenshots a month with no card.
FAQ
Does setContent() return the HTML?
No. It returns a promise that resolves when the configured wait threshold completes. Use page.content() to read the page markup.
Can I wait for multiple lifecycle events?
Yes. Pass an array to waitUntil; all listed events must fire before the call completes.
Does a successful setContent() call mean my app is ready?
Only if the configured threshold represents the readiness your next step needs. For client-rendered application state, wait for that state explicitly.
Official Puppeteer references
- Page.setContent
- SetContentWaitForOptions
- WaitForOptions and PuppeteerLifeCycleEvent
- Page.waitForNetworkIdle and WaitForNetworkIdleOptions
- Page interactions and Page.content
Option types can change across Puppeteer releases. Check the API reference and types for the version installed in your project.


