Handling Lazy-Loaded Content in Automated Screenshots
You set up Puppeteer, navigate to a page, call page.screenshot() , and the bottom half of your image is blank placeholder boxes. Welcome to lazy loading. Most modern sites defer images and heavy content until the user scrolls. Your headless browser never scrolls. So those elements never load. Here's how to deal with it. The scroll trick The most common fix is to programmatically scroll down the page before taking the screenshot: async function scrollToBottom ( page ) { await page . evaluate ( async () => { const delay = ms => new Promise ( r => setTimeout ( r , ms )); const distance = 300 ; while ( window . scrollY + window . innerHeight < document . body . scrollHeight ) { window . scrollBy ( 0 , distance ); await delay ( 150 ); } window . scrollTo ( 0 , 0 ); }); } await page . goto ( " https://example.com " , { waitUntil : " networkidle2 " }); await scrollToBottom ( page ); await page . waitForTimeout ( 1000 ); await page . screenshot ({ fullPage : true }); The 150ms delay between scrolls gives IntersectionObserver -based lazy loaders time to trigger. Too fast and you'll scroll past elements before they start loading. That final waitForTimeout after scrolling back to top lets any remaining images finish rendering. Not elegant, but necessary. Why networkidle2 isn't enough You'd think waitUntil: "networkidle2" would handle this. It waits until there are no more than 2 network connections for 500ms. But lazy-loaded images haven't even been requested yet at that point — they're waiting for a scroll event that never happens. networkidle2 only helps with content that loads on page init. For scroll-triggered content, you need the scroll. The loading="eager" override Some sites use the native loading="lazy" attribute. You can override it before images load: await page . evaluateOnNewDocument (() => { Object . defineProperty ( HTMLImageElement . prototype , " loading " , { set : function ( val ) { this . setAttribute ( " loading " , " eager " ); }, get : function () { retu