Windows 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 reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWait for navigation to reach an explicit boundary, then wait for the page state your image actually needs, and only then call page.screenshot(). For many pages, waitUntil: 'networkidle2' is a practical starting point; dynamic applications often need page.waitForSelector(), page.waitForFunction(), or a short post-navigation network-idle wait as well.
The reliable Puppeteer sequence
This complete example launches Chromium, opens a URL, waits for most network activity to settle, waits for a page-specific readiness marker, captures a full-page PNG, and closes the browser even when an error occurs.
import puppeteer from 'puppeteer';
const url = 'https://example.com';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto(url, {
waitUntil: 'networkidle2',
timeout: 30_000,
});
// Use this only when the site exposes a real visual-readiness marker.
// await page.waitForSelector('[data-testid="report-ready"]', {
// visible: true,
// timeout: 15_000,
// });
await page.screenshot({
path: 'screenshot.png',
fullPage: true,
});
} finally {
await browser.close();
}
page.goto() resolves at the navigation boundary selected by waitUntil. The screenshot guide’s standard pattern uses networkidle2 followed by page.screenshot(). Always await the screenshot promise; otherwise your process can exit before the file is written.
Choose the right navigation boundary
The waitUntil value describes a browser event or network condition, not a guarantee that every visual detail is finished.
Recommended Free Tools
#1 Best Overall
| Boundary | What it observes | Use it when | Important limitation |
|---|---|---|---|
domcontentloaded |
The initial HTML has been parsed. | Your shot depends on the DOM structure but not on images, fonts, or later resources. | Images, web fonts, and client-rendered content may still be missing. |
load |
The page’s load event has fired. | The site’s load event is a suitable readiness boundary. | JavaScript may continue rendering after the event. |
networkidle2 |
Network activity has settled to no more than two active requests for the required idle period. | You want a general screenshot boundary and the page does not maintain many long-lived requests. | It can still resolve before a particular component is visually ready. |
networkidle0 |
There are no active network requests for the required idle period. | Zero in-flight requests is realistic for the target. | Analytics, polling, streaming, or WebSocket connections can prevent it from resolving. |
For a conventional document, start with networkidle2. If it times out because the page polls or streams data, move to domcontentloaded or load and add a condition tied to the content you need.
Wait for resources that arrive after navigation
A navigation can finish while a single-page application is still fetching data. In that case, perform a separate network-idle wait after goto():
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com/dashboard', {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
await page.waitForNetworkIdle({
idleTime: 500,
timeout: 10_000,
});
await page.screenshot({ path: 'dashboard.png' });
} finally {
await browser.close();
}
page.waitForNetworkIdle() resolves after the configured idle period and always waits at least that long. It is a traffic measurement, however, so it does not prove that the exact chart, image, or text you want is present.
Wait for the exact visual state
Wait for a visible selector
If the application adds a reliable marker when rendering finishes, wait for that marker instead of guessing with a delay:
await page.goto('https://example.com/report', {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
await page.waitForSelector('[data-testid="report-ready"]', {
visible: true,
timeout: 15_000,
});
await page.screenshot({
path: 'report.png',
fullPage: true,
});
The selector should represent the state that the screenshot must contain, such as a rendered table or a “loaded” marker. A selector for a permanent shell element is not sufficient.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Wait for an application predicate
When readiness is stored in a JavaScript property, use waitForFunction():
await page.goto('https://example.com/app', {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
await page.waitForFunction(
() => document.querySelectorAll('.result-row').length >= 20,
{ timeout: 15_000 },
);
await page.screenshot({ path: 'results.png', fullPage: true });
Keep the predicate specific and bounded. If the condition can never become true, Puppeteer should fail with a timeout rather than silently creating an incomplete image.
Use a bounded delay only as a fallback
A fixed delay can accommodate an animation or a third-party widget when no selector or predicate exists, but it is a timing guess. Prefer a page-specific condition and retain the delay only when the visual transition itself is the requirement.
Synchronize clicks that trigger navigation
For links, form submissions, or buttons that navigate, begin waiting before the action. Starting afterward can miss a fast navigation:
await Promise.all([
page.waitForNavigation({
waitUntil: 'networkidle2',
timeout: 30_000,
}),
page.click('a.next'),
]);
await page.screenshot({
path: 'next.png',
fullPage: true,
});
If the click updates the current document without navigation, do not use waitForNavigation(); wait for the selector, predicate, or network condition produced by that update instead.
Rank #3
A production-ready helper
Centralizing navigation, readiness, timeout handling, and diagnostics makes batch captures easier to operate:
import puppeteer from 'puppeteer';
async function capture(url, outputPath) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.setDefaultTimeout(15_000);
try {
const response = await page.goto(url, {
waitUntil: 'networkidle2',
timeout: 30_000,
});
if (!response) {
throw new Error(`No main-resource response for ${url}`);
}
await page.screenshot({
path: outputPath,
fullPage: true,
});
} catch (error) {
await page.screenshot({ path: `${outputPath}.error.png` }).catch(() => {});
throw error;
} finally {
await browser.close();
}
}
await capture('https://example.com', 'example.png');
The diagnostic image is useful when a navigation or readiness wait fails. In a worker, record the URL, wait mode, timeout, and error message with the job so an incomplete capture is distinguishable from a successful one.
Why screenshots still miss images or text
Lazy-loaded content is below the initial viewport
Some sites request images only after an element approaches the viewport. A full-page screenshot does not guarantee that every lazy image has been requested before capture. Wait for a site-provided “all content loaded” marker, scroll through the page to trigger lazy loading, or capture after the relevant image elements report completion.
Network idle does not equal visual idle
Fonts can swap after the network quiets, a client-side render can occur in a microtask, and a CSS animation can still be moving. A selector or predicate that represents the finished state is more precise than a global network condition.
The page never becomes idle
Polling, analytics, streaming, and WebSockets can keep requests active indefinitely. Use domcontentloaded or load, then wait for the component you need with a finite timeout. Do not increase a timeout forever to compensate for a condition that is structurally impossible.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
A cookie banner or modal covers the page
This is a page-state issue, not a Puppeteer wait issue. Accept or dismiss the banner in your script, or hide the relevant element before capture. If the banner is produced by an external consent platform, its timing can vary between runs, so use a bounded selector wait and a fallback path.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsTroubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
Navigation timeout of 30000 ms exceeded |
The server is slow, the URL is unreachable, or the selected network-idle condition never occurs. | Verify the URL, increase the timeout for this site, or use domcontentloaded/load plus an application-specific wait. |
Waiting for selector ... failed |
The selector is wrong, the element is inside a frame, or the application never reaches that state. | Inspect the rendered DOM, target the correct frame, confirm the state transition, and keep a finite timeout. |
| The screenshot is mostly blank | The capture ran before client rendering, or a script failed. | Capture an error artifact, inspect console/page errors, and wait for a real readiness marker rather than adding an arbitrary long delay. |
| Images are absent | Lazy loading, blocked requests, failed image URLs, or a font/image request still in progress. | Trigger lazy loading, verify image completion, check request failures, and choose a wait condition that covers the required resources. |
waitForNavigation() hangs after a click |
The click performs an in-page update instead of navigation. | Replace it with waitForSelector(), waitForFunction(), or waitForNetworkIdle() for the update. |
networkidle0 never resolves |
Persistent polling, streaming, analytics, or a WebSocket keeps a connection open. | Use networkidle2 or an event-based boundary, then wait for the exact content required. |
Performance, reliability, and cost considerations
Use the least expensive wait that is correct
domcontentloaded usually returns sooner than a network-idle condition, but speed is not a benefit if the resulting image is incomplete. For static pages, load or networkidle2 may be enough. For applications, a precise selector often avoids waiting for unrelated background requests.
Bound every asynchronous operation
Set navigation and readiness timeouts appropriate to the site. A timeout should produce an error and, where possible, a diagnostic screenshot or page log. Treat retries carefully: retry transient network failures, but do not retry a permanently false selector without changing the condition.
Keep browser lifecycle explicit
Reuse a browser process for a controlled batch when startup cost matters, but create isolated pages and close them after each job. Always close the browser in a finally block so failed captures do not leak Chromium processes.
Understand what Puppeteer does not promise
No single wait mode guarantees that every image, font, animation, advertisement, or client-rendered component is complete. Readiness is application-specific. The most reliable boundary is the one that corresponds to the visual state your user needs.
Free tools Windows power users keep installed
One-click scans. No signup required.
Or skip the browser setup
ScreenshotNeo provides a one-request screenshot API when you do not want to maintain Chromium, navigation waits, and page cleanup. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and whether it was billed. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.
Use the API documentation at https://screenshotneo.com/docs/ for authentication and options. This cURL request saves a WebP image:
Best Value
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,
)
r.raise_for_status()
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}`);
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()));
ScreenshotNeo includes full-page and element capture, dark mode, device presets, custom viewports and retina scale, PDF output, HTML/CSS rendering, custom JavaScript and CSS, clicks, selector waits, delays, network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous webhooks, bulk capture for up to 100 URLs per call, a usage API, an OpenAPI specification, and compatibility with parameter names used by other screenshot APIs. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Can I combine more than one readiness condition?
Yes. A common pattern is navigation with domcontentloaded, followed by waitForNetworkIdle(), followed by a selector or predicate. Each condition should serve a distinct purpose and have its own timeout.
What does a null response from page.goto() mean?
The navigation can resolve without a main-resource response for certain navigation types. If your workflow requires an HTTP response, check for a null value and handle it as a diagnostic or application error.
Should I wait before every screenshot?
Wait whenever navigation or an interaction can change the page. If several screenshots are taken from the same already-ready state, capture them without repeating a global wait, while still synchronizing any action that changes the content.
Frequently Asked Questions
Can I combine more than one readiness condition?
Yes. A common pattern is navigation with domcontentloaded, followed by waitForNetworkIdle(), followed by a selector or predicate. Each condition should serve a distinct purpose and have its own timeout.
What does a null response from page.goto() mean?
The navigation can resolve without a main-resource response for certain navigation types. If your workflow requires an HTTP response, check for a null value and handle it as a diagnostic or application error.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Should I wait before every screenshot?
Wait whenever navigation or an interaction can change the page. If several screenshots are taken from the same already-ready state, capture them without repeating a global wait, while still synchronizing any action that changes the content.
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.

