Use the wait that represents the condition your next operation needs. For a click that changes the current URL, register page.waitForNavigation() at the same time as the click. For content, wait for a selector or an in-page predicate. For a request, response, or quiet network, wait for that event explicitly. For a new tab, wait for a browser target. Use a fixed timer only when elapsed time itself is the requirement.
Every Puppeteer wait consumes part of the Firebase function’s execution budget. Configure the function timeout for the trigger you use, and keep each individual wait finite so a failed page cannot hold a function until the outer limit.
What “wait” means in Puppeteer
“Wait for the page” is ambiguous. Puppeteer can wait for several different events, and they do not prove the same thing.
| Need | Use | What it proves | Typical failure |
|---|---|---|---|
| The current document navigated | page.waitForNavigation() |
A navigation event completed according to the selected lifecycle condition. | Timeout when the click did not navigate, navigation was blocked, or the page is a single-page app. |
| An element exists or is visible | page.waitForSelector() |
The selector matched; visibility options can require that it is visible. | Timeout because the selector is wrong, content is inside an iframe, or rendering failed. |
| An application state is true | page.waitForFunction() |
A page-context function returned a truthy value. | Timeout because the condition never becomes true or references unavailable page variables. |
| A request or response occurred | Request/response waiters | The specified network event happened, not that every UI update finished. | Wrong URL predicate, method, status, or request fired before the waiter was installed. |
| Network activity became quiet | page.waitForNetworkIdle() |
Network stayed below the configured concurrency for at least the configured idle period. | Analytics, polling, streaming, or ads prevent idleness; an idle network still may not mean the UI is ready. |
| A popup or new tab opened | browserContext().waitForTarget() |
A new browser target matching your predicate exists. | The click opened no target, the predicate is too broad, or the target is not yet associated with a page. |
| A real elapsed interval | new Promise(resolve => setTimeout(resolve, ms)) |
Only that the timer elapsed. | It wakes before the page is ready or wastes the function budget. |
The current Puppeteer documentation marks Page.waitForTimeout obsolete and recommends a native timer only when a fixed delay is genuinely required. A state-based wait is normally both faster and more reliable.
#1 Best Overall
- The world’s fastest gaming processor, built on AMD ‘Zen5’ technology and Next Gen 3D V-Cache.
- 8 cores and 16 threads, delivering +~16% IPC uplift and great power efficiency
- 96MB L3 cache with better thermal performance vs. previous gen and allowing higher clock speeds, up to 5.2GHz
- Drop-in ready for proven Socket AM5 infrastructure
- Cooler not included
Waiting after a click that navigates
Start the navigation wait and the action together. Waiting for the click first can lose a fast navigation before the separate waiter is registered.
const [response] = await Promise.all([
page.waitForNavigation({
waitUntil: 'domcontentloaded',
timeout: 30_000,
}),
page.click('a.next'),
]);
if (!response) {
throw new Error('The action did not produce a navigation response');
}
domcontentloaded means the document was parsed; it does not guarantee that images, client-side data, or a loading spinner are finished. If the next operation needs rendered application content, follow navigation with a condition for that content:
await page.waitForSelector('[data-ready="true"]', {
visible: true,
timeout: 10_000,
});
Some links open a new target rather than navigating the current page. Use the popup pattern below instead of waiting on the original page.
Waiting for a selector or visible content
Use waitForSelector(selector, options) when the DOM element itself is the contract between steps. The documented default timeout for selector waits is 30 seconds; set a shorter value when a function should fail quickly or a longer value when the page is known to be slow.
Free tools Windows power users keep installed
One-click scans. No signup required.
const submit = await page.waitForSelector('button[type="submit"]', {
visible: true,
timeout: 15_000,
});
await submit.click();
A successful selector wait does not mean the element is enabled, stable, or backed by loaded data. Check application-specific attributes when needed:
Rank #2
- AMD Ryzen 9 9950X3D Gaming and Content Creation Processor
- Max. Boost Clock : Up to 5.7 GHz; Base Clock: 4.3 GHz
- Form Factor: Desktops , Boxed Processor
- Architecture: Zen 5; Former Codename: Granite Ridge AM5
await page.waitForSelector('[aria-busy="false"]', { timeout: 10_000 });
await page.waitForFunction(
() => document.querySelector('#results')?.children.length > 0,
{ timeout: 10_000 }
);
If the element is inside an iframe, obtain the frame and wait there. If it is created by a shadow DOM component, use a page predicate that traverses the relevant shadow root.
Waiting for an in-page condition
page.waitForFunction() is appropriate when no single selector describes readiness: a JavaScript variable changes, a counter reaches a value, or a framework sets a state flag.
await page.waitForFunction(
() => window.appState?.report?.status === 'complete',
{ timeout: 20_000, polling: 'mutation' }
);
The function runs in the page context, so it can access window and the document but not ordinary variables in your Firebase process unless you pass them as arguments. Keep the predicate cheap and deterministic; a predicate that throws or never becomes true ends in a timeout.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Waiting for requests and network idle
Wait for the exact request or response when that is the meaningful event. Install the waiter before the action that triggers it.
const responsePromise = page.waitForResponse(
response => response.url().endsWith('/api/report') &&
response.request().method() === 'GET' &&
response.status() === 200,
{ timeout: 20_000 }
);
await page.click('#load-report');
const response = await responsePromise;
Use waitForNetworkIdle when you specifically need a quiet period, for example before taking a screenshot of a page whose resources load in bursts:
Rank #3
- Can deliver fast 100 plus FPS performance in the world's most popular games, discrete graphics card required
- 6 Cores and 12 processing threads, bundled with the AMD Wraith Stealth cooler
- 4.2 GHz Max Boost, unlocked for overclocking, 19 MB cache, DDR4-3200 support
- For the advanced Socket AM4 platform
await page.waitForNetworkIdle({
idleTime: 500,
concurrency: 0,
timeout: 20_000,
});
Network idle is not proof that a single-page application has finished updating. Polling, WebSockets, analytics, and long-lived requests can prevent idle; conversely, the network can be quiet while a client-side render is still pending. Combine a network wait with a selector or page predicate when both conditions matter.
Waiting for a popup or new tab
A call to window.open creates a new browser target. Waiting for navigation on the original page will not give you the popup. Match the expected URL (and, if useful, the opener) with BrowserContext.waitForTarget:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallconst context = page.browserContext();
const targetPromise = context.waitForTarget(
target => target.url().startsWith('https://example.com/result'),
{ timeout: 20_000 }
);
await page.click('button.open-result');
const target = await targetPromise;
const popup = await target.page();
if (!popup) throw new Error('The target has no page');
await popup.waitForSelector('#result', { timeout: 10_000 });
Use a precise predicate. A predicate that only checks the target type can resolve the first unrelated tab, especially in a reused browser context.
Using a fixed delay safely
Sometimes time itself is required: allowing an animation to finish, spacing requests to an external service, or reproducing a scheduled interaction. In current Puppeteer documentation, Page.waitForTimeout is obsolete. Use a native timer and keep the delay bounded:
await new Promise(resolve => setTimeout(resolve, 1_000));
Do not replace a missing readiness condition with “sleep five seconds.” A slow response can exceed the guess, while a fast response leaves unnecessary latency. Prefer a selector, predicate, request, or network condition and reserve timers for behavior that is intentionally time-based.
Rank #4
- Pure gaming performance with smooth 100+ FPS in the world's most popular games
- 6 Cores and 12 processing threads, based on AMD "Zen 5" architecture
- 5.4 GHz Max Boost, unlocked for overclocking, 38 MB cache, DDR5-5600 support
- For the state-of-the-art Socket AM5 platform, can support PCIe 5.0 on select motherboards
- Cooler not included
A Firebase Functions example with explicit waits
The following HTTP function shows launch, navigation, content readiness, and cleanup. Adapt browser installation and launch options to the exact Puppeteer package and Firebase runtime you deploy. Puppeteer guarantees compatibility with its bundled browser; using an unrelated executable path is at your own risk.
const { onRequest } = require('firebase-functions/v2/https');
const puppeteer = require('puppeteer');
exports.capture = onRequest(
{ timeoutSeconds: 120, memory: '1GiB' },
async (req, res) => {
let browser;
try {
const url = String(req.query.url || 'https://example.com');
browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
page.setDefaultTimeout(15_000);
page.setDefaultNavigationTimeout(30_000);
await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
await page.waitForSelector('main', {
visible: true,
timeout: 15_000,
});
const title = await page.title();
res.status(200).json({ title, url: page.url() });
} catch (error) {
console.error(error);
res.status(504).json({ error: 'Page was not ready before the wait timed out' });
} finally {
if (browser) await browser.close();
}
}
);
Set the function’s timeout above the longest legitimate browser workflow, but do not use the maximum merely because it is available. A browser that hangs should fail at its own navigation or condition timeout, leaving time for error handling and response delivery.
How long can a Puppeteer wait run in Firebase Functions?
The function timeout is the outer ceiling for all work: startup, browser launch, navigation, waits, processing, and cleanup. Firebase currently documents different maximum durations by trigger:
| Firebase trigger | Documented maximum |
|---|---|
| HTTP and callable | 3,600 seconds |
| Scheduled and task queue | 1,800 seconds |
| Other event-driven functions | 540 seconds |
These are platform limits, not recommended wait values. Configure timeoutSeconds in runtime options for the function type you actually use. Firebase treats runtime options in source as the source of truth by default; console or CLI changes can be overridden unless you deliberately use the documented external-change preservation behavior. Verify the current limits for your region, generation, and trigger when deploying.
A practical budget includes a navigation timeout, a content timeout, and a small margin for logging and browser shutdown. For example, a 120-second HTTP function might use a 30-second navigation timeout and 15-second selector timeout, with an explicit overall policy to reject unusually slow pages.
Best Value
- Processor provides dependable and fast execution of tasks with maximum efficiency.Graphics Frequency : 2200 MHZ.Number of CPU Cores : 8. Maximum Operating Temperature (Tjmax) : 89°C.
- Ryzen 7 product line processor for better usability and increased efficiency
- 5 nm process technology for reliable performance with maximum productivity
- Octa-core (8 Core) processor core allows multitasking with great reliability and fast processing speed
- 8 MB L2 plus 96 MB L3 cache memory provides excellent hit rate in short access time enabling improved system performance
Common timeout and deployment failures
“Navigation timeout exceeded”
- Cause: the URL is slow, blocked, redirecting indefinitely, or the selected lifecycle event never occurs.
- Fix: verify the URL from the deployed region, set a finite navigation timeout, choose the lifecycle event that matches your need, and add a selector or predicate for application readiness.
The click happened but the wait timed out
- Cause: the click did not navigate, or the waiter was registered after the click.
- Fix: use
Promise.allwithwaitForNavigationandclick. If it is a single-page update, wait for the resulting selector, request, or page state instead.
The selector never appears
- Cause: wrong selector, iframe or shadow DOM, authentication failure, or a page that rendered an error state.
- Fix: capture the final URL and HTML, inspect frames, check response status, and wait for an error selector as a separate branch.
Network idle never arrives
- Cause: polling, WebSockets, analytics, downloads, or advertisements keep requests active.
- Fix: wait for the specific response and visible content instead; if idle is still required, tune its concurrency and idle time and retain a hard timeout.
The popup wait resolves incorrectly
- Cause: a broad target predicate matched an existing tab or an unrelated popup.
- Fix: match the expected URL prefix, target type, and opener where possible, and create the promise before the click.
The function is killed before Puppeteer finishes
- Cause: the configured Firebase timeout is shorter than the browser workflow, or the trigger has a lower platform maximum.
- Fix: set an appropriate
timeoutSeconds, shorten individual waits, close the browser infinally, and account for cold-start and cleanup time.
Browser launch fails after deployment
- Cause: the deployed package cannot find a compatible browser or an executable path was copied from another environment.
- Fix: use the browser bundled and supported by your installed Puppeteer setup, follow the runtime’s packaging requirements, and avoid assuming one Chromium recipe works for every Firebase generation.
Performance and reliability checklist
- Register event waits before the action that causes them.
- Use the narrowest condition that proves the next step is safe.
- Set explicit navigation, selector, request, and predicate timeouts.
- Keep a separate outer function budget with margin for cleanup.
- Log the URL, wait type, elapsed time, and timeout cause without logging secrets.
- Close pages and browsers in
finally, including error paths. - Reuse a browser only when your isolation and concurrency design permits it; otherwise launch per invocation and accept the cold-start cost.
- Treat retries carefully: a retry can duplicate a click or external request unless the operation is idempotent.
Or skip the browser setup
If your goal is a reliable website image or PDF rather than browser automation, ScreenshotNeo provides a GET endpoint and an MCP server. It accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be disabled individually. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
One call returns PNG, JPEG, WebP, or PDF. The service supports full-page lazy-image loading, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page options, custom CSS and JavaScript, pre-capture clicks, selector or network-idle waits, blocking rules, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
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)
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}`);
See the ScreenshotNeo API documentation for parameters. An MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is included on every plan. Create a free ScreenshotNeo account.
Frequently Asked Questions
Should I use waitUntil: 'networkidle0' in goto?
Only when network quiet is the condition you need. Many sites keep analytics, polling, or sockets open, so a specific response plus a readiness selector is often more dependable.
What timeout should I choose for waitForSelector?
The documented default is 30 seconds. Choose a value based on your page’s normal latency and the Firebase function budget, then keep a separate outer function timeout.
Can a navigation wait detect a single-page-app route change?
Not necessarily. If the URL changes without a document navigation, wait for the route’s distinctive selector, application state, or API response.
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.

