How to capture webpage screenshot after images finish loading using Puppeteer

I’m trying to use Puppeteer to capture a full page screenshot but only after all the images on the page have completely loaded. My current approach doesn’t seem to be working properly and I’m not sure what I’m missing.

Here’s what I have so far using a news website as a test case:

const puppeteer = require('puppeteer');

(async () => {
    const browserInstance = await puppeteer.launch();
    const webPage = await browserInstance.newPage();
    await webPage.goto('https://www.reddit.com/');

    await webPage.setViewport({width: 1920, height: 1080});

    await webPage.evaluate(() => {
        return Promise.resolve(window.scrollTo(0, document.body.scrollHeight));
    });

    await webPage.waitForTimeout(2000);

    await webPage.evaluate(() => {
        var allImages = document.querySelectorAll('img');

        function waitForAllImages() {
            var imagePromises = [];

            function checkImageLoad(imageElement) {
                return new Promise(function(resolve, reject) {
                    if (imageElement.complete) {
                        resolve(imageElement);
                    }
                    imageElement.addEventListener('load', function() {
                        resolve(imageElement);
                    });
                    imageElement.addEventListener('error', function(err) {
                        resolve(imageElement);
                    });
                });
            }

            for (var j = 0; j < allImages.length; j++) {
                imagePromises.push(checkImageLoad(allImages[j]));
            }

            return Promise.all(imagePromises);
        }

        return waitForAllImages();
    });

    await webPage.screenshot({path: 'website-capture.png', fullPage: true});

    browserInstance.close();
})();

Any ideas what might be wrong with this approach?

Your code structure looks decent, but there’s a critical issue - you’re not waiting for the promise your evaluate function returns. The evaluate call returns a promise that resolves when images finish loading, but you’re not awaiting it. Add await before your webPage.evaluate() call. Also, use waitForLoadState('networkidle') instead of that arbitrary 2-second timeout. This waits until there’s no network requests for 500ms, which usually means images are done loading. One more thing - Reddit uses lazy loading heavily, so scrolling once won’t trigger all images to load. You’ll need to scroll progressively down the page so all images in viewport actually start loading before your image checking logic runs.

Your code isn’t properly awaiting the image loading promise inside page.evaluate(). Puppeteer needs to handle any returned promises correctly. Switch to page.waitForFunction() instead of page.evaluate() for checking if images are done loading. Also try page.waitForLoadState('networkidle') - it waits until no network requests happen for 500ms, which catches lazy-loaded images way better than checking each img element manually. I’ve had better luck combining networkidle with a small timeout after. Works great on dynamic sites like Reddit where images load as you scroll.

The issue is you’re scrolling to the bottom once and expecting everything to load. Reddit uses lazy loading - images won’t load until they’re actually visible on screen. Don’t scroll to the end right away. Instead, scroll progressively in small chunks with pauses between each step. This lets the browser detect which images are now visible and start loading them. I scroll by viewport height with a 500ms wait between scrolls, repeating until no new images show up. After progressive scrolling, your image checking should work since all images will have started loading. Also throw in page.waitForLoadState(‘domcontentloaded’) before scrolling to make sure the initial DOM is ready.

Had the exact same headache with dynamic sites. The scrolling strategy is your issue - Reddit’s infinite scroll keeps loading new images as you scroll down. Don’t jump straight to the bottom. Instead, intercept network requests for the images. Use page.setRequestInterception(true) and track image requests, then wait until all pending requests finish. This catches lazy-loaded content way better than checking the DOM. Also try page.waitForResponse() to monitor when specific image endpoints are done. The network approach beats DOM manipulation because it catches images that aren’t even in the DOM yet but are queued to load.

I’ve dealt with this exact problem for years. You’re fighting modern web architecture here.

Reddit and most sites load images as you scroll and interact. Your code checks for images that haven’t even started loading yet.

Skip wrestling with Puppeteer’s quirks and complex promise chains. Just automate the whole workflow instead. Build a screenshot service that handles lazy loading, progressive scrolling, and image detection automatically.

I use this for monitoring product pages and competitor analysis. Set up triggers that scroll smartly, wait for network idle, and capture screenshots when everything’s actually loaded.

The automation catches edge cases like slow CDNs, failed loads, and dynamic content that regular Puppeteer scripts miss.

Way more reliable than debugging JavaScript promises in browser contexts.

try page.waitForSelector('img', {state: 'attached'}) before your image checking logic. reddit uses heavy lazy loading, so scrolling won’t catch images that don’t start loading until they’re nearly visible. also drop the timeout and just await the evaluate properly.