To capture lazy-loaded images in a full-page Puppeteer screenshot, scroll through the page in viewport-sized steps so the page can load images as they approach the viewport, check that the images you need have loaded, then capture with fullPage: true. A navigation wait such as networkidle2 alone does not guarantee that off-screen images have loaded.
Capture the page after triggering lazy loading
Page.screenshot() is Puppeteer’s screenshot method. Set fullPage: true to request the full page; the documented default is false. [Puppeteer screenshots guide; ScreenshotOptions; Page.screenshot()]
The following Node.js example scrolls in viewport-sized increments, waits for images in or near each viewport to load or fail, and makes a second pass if loading images expanded the document. It then checks the page’s image elements before taking the screenshot. Install Puppeteer in your project with npm install puppeteer and save this as a JavaScript file:
const puppeteer = require('puppeteer');
async function waitForImagesInView(page, timeoutMs = 5000) {
await page.evaluate(async (timeout) => {
const images = [...document.images].filter((img) => {
const rect = img.getBoundingClientRect();
return rect.bottom >= 0 && rect.top <= window.innerHeight;
});
await Promise.all(images.map((img) => {
if (img.complete) return Promise.resolve();
return new Promise((resolve) => {
const finish = () => {
clearTimeout(timer);
img.removeEventListener('load', finish);
img.removeEventListener('error', finish);
resolve();
};
const timer = setTimeout(finish, timeout);
img.addEventListener('load', finish, { once: true });
img.addEventListener('error', finish, { once: true });
});
}));
}, timeoutMs);
}
async function scrollToLoadImages(page, maxPasses = 3) {
const viewportHeight = page.viewport().height;
for (let pass = 0; pass < maxPasses; pass++) {
const heightBefore = await page.evaluate(() =>
document.documentElement.scrollHeight
);
for (let y = 0; y < heightBefore; y += viewportHeight) {
await page.evaluate((position) => window.scrollTo(0, position), y);
await waitForImagesInView(page);
}
const heightAfter = await page.evaluate(() =>
document.documentElement.scrollHeight
);
if (heightAfter <= heightBefore) break;
}
await page.evaluate(() => window.scrollTo(0, 0));
}
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage({
viewport: { width: 1365, height: 900 },
});
await page.goto('https://example.com', {
waitUntil: 'networkidle2',
timeout: 60000,
});
await scrollToLoadImages(page);
const imageStatus = await page.evaluate(() =>
[...document.images].map((img) => ({
src: img.currentSrc || img.src,
complete: img.complete,
naturalWidth: img.naturalWidth,
}))
);
const failedImages = imageStatus.filter(
(img) => !img.complete || img.naturalWidth === 0
);
if (failedImages.length) {
console.warn('Images not successfully loaded:', failedImages);
}
await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
await browser.close();
}
})();
Replace https://example.com with the page you are authorized to capture. The image check reports page image elements that did not successfully load; whether they must block your capture depends on your use case. It does not detect every visual asset: CSS background images, canvas content, and images inserted or managed by site-specific scripts may need separate checks.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
Why scrolling and image checks matter
Full-page capture is not lazy-load triggering
fullPage: true requests a screenshot of the full page, but it does not itself guarantee that every off-screen image has been requested or rendered. Scrolling can prompt native lazy loading, intersection observers, and other viewport-triggered site behavior. Puppeteer provides page interaction primitives; the site defines what scrolling causes. [Puppeteer page interactions]
Navigation completion is not image completion
networkidle2 means there are no more than two network connections for at least 500 ms. networkidle0 waits for no active connections for at least 500 ms. These are lifecycle conditions, not guarantees that every image has loaded, decoded, or appeared in the page. [PuppeteerLifeCycleEvent; WaitForOptions; WaitForNetworkIdleOptions]
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
In the example, the navigation wait handles initial loading, scrolling encourages viewport-triggered requests, and image-specific checks give you evidence about ordinary <img> elements. A load or error event ends the wait for that image; naturalWidth helps distinguish a successfully loaded image from a failed one. The timeout prevents one stalled image from blocking the whole capture indefinitely.
Choose a wait strategy for the page
| Strategy | What it waits for | When it helps | What it does not establish |
|---|---|---|---|
networkidle2 |
At most two network connections for at least 500 ms. | A practical initial navigation wait for pages that settle while allowing a small number of persistent requests. | That lazy images below the fold were triggered or rendered. |
networkidle0 |
No active network connections for at least 500 ms. | Pages that become fully quiet and where waiting for that stricter condition is appropriate. | That every image is present; persistent polling or other activity can also prevent the condition. |
| Scroll plus image checks | The script scrolls through the document and waits for visible image elements to load, fail, or reach a timeout. | Pages where images load as they approach the viewport. | That CSS backgrounds, canvas content, or custom application rendering are complete. |
The 500 ms interval is part of Puppeteer’s documented lifecycle behavior, not a recommended universal delay for image loading. Choose based on the site’s behavior, and verify the actual content you need.
Rank #3
Adapt the method to the site
- Use a longer image timeout if images are slow but do eventually load. Short timeouts may let the script move on while requests are still in progress.
- Repeat scrolling when content grows. The example compares document height before and after each pass and limits repeats to avoid looping forever on pages that continuously append content.
- Check a specific image or selector if only a subset matters. The example’s
document.imagescheck covers image elements, not CSS backgrounds or every custom lazy-loading implementation. - Account for site-specific triggers. Some pages need a particular scroll direction, a click, authentication, or waiting for a selector before images appear. Adjust the interaction and wait to match the page.
- Keep an eye on the capture dimensions. Full-page capture produces an image of the document’s full dimensions, which can be large on very long pages. If your consumer has image-size limits, consider capturing sections or using an appropriate output workflow.
Troubleshoot missing or incomplete images
The screenshot still has blank image areas
Confirm that scrolling reached the relevant portion of the document and that the site’s lazy-load trigger is actually viewport-based. Inspect currentSrc, complete, and naturalWidth for affected image elements. A complete image with a zero natural width likely failed; an incomplete image may need more wait time or a page-specific trigger.
networkidle0 never completes
A page may keep connections active for polling, analytics, or other background work. Use networkidle2 if appropriate, or use a targeted selector or image wait instead of requiring total network silence. Puppeteer’s network-idle thresholds describe connection activity, not page correctness.
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
The document gets taller while scrolling
Lazy content may be inserted only after earlier images or sections load. The example makes another pass when height increases, up to its configured maximum. Raise that limit only if the page’s behavior warrants it; endless-feed pages may never reach a stable height.
Images are loaded but visually incomplete
An image load event and nonzero natural width do not prove that every application-level visual update is finished. If the page animates, swaps sources, or renders content through canvas or CSS, wait for the relevant selector or state and inspect that content separately before capture.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Best Value
The capture times out or uses too much memory
Very long pages can take substantial time and produce large screenshots. Increase the navigation timeout only when a slow but valid page is expected; for large documents, capture smaller sections or reduce output dimensions where suitable. Always close the browser in a finally block so failures do not leave the process running.
Or skip the browser setup
ScreenshotNeo offers a one-request screenshot API; its docs are at ScreenshotNeo documentation. For example, this cURL request captures a page as WebP:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and each response reports the page verdict and billing status in headers. It also has an MCP server for AI agents, with tools including 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. See ScreenshotNeo for details and sign up free.
FAQ
Does Puppeteer have a built-in option to load every lazy image?
The documented screenshot option requests a full-page capture, but Puppeteer’s screenshot API does not prescribe a universal lazy-image loading helper. Trigger and verify loading with interactions suited to the page.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Should I use networkidle0 or networkidle2?
Use the condition that fits the site’s network behavior. Neither one verifies that lazy-loaded image elements have successfully rendered.
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.

