I’m working with a single page app that has vertical scrolling content. When I try to capture a screenshot using Puppeteer, it only grabs what’s currently visible in the viewport instead of the entire scrollable content. I need to get a screenshot that includes all the content from top to bottom of the page. Here’s what I’m currently using:
const browserInstance = await puppeteer.launch(config);
const webPage = await browserInstance.newPage();
await webPage.goto(targetUrl);
await webPage.screenshot({ path: 'screenshot.png', fullPage: true });
await browserInstance.close();
Even with the fullPage option set to true, I’m still getting partial screenshots. What am I missing here? Has anyone dealt with this issue before with SPAs that have dynamic content loading?
This happens because Puppeteer grabs the full page based on document height, but SPAs often haven’t rendered everything yet. I’ve had good luck setting a larger viewport before navigation - it tricks the page into rendering more content upfront. Try await webPage.setViewport({ width: 1920, height: 10000 }) before your goto call. This forces a much taller viewport and usually loads lazy content without messy scrolling logic. Also use await webPage.goto(targetUrl, { waitUntil: 'networkidle2' }) instead of the default ‘load’ event. This waits for AJAX calls and dynamic imports to finish before screenshotting. Large viewport + network idle usually captures everything in one shot without extra complexity.
good point! a wait can help! i usually use await webPage.waitForTimeout(3000) just to be safe, or even better, wait for a specific element to load like the footer with await webPage.waitForSelector('footer'). that way, everything should be loaded before the screenshot.
Yeah, this is a classic SPA problem. Dynamic content loads as you scroll, but Puppeteer thinks the page is done loading.
I’ve run into this tons of times. The fix is scrolling down in chunks to trigger the lazy loading, waiting for new content, then taking your screenshot.
But setting up all that scroll logic is a pain. You’ll end up writing custom code for different loading patterns, infinite scroll detection, timing - it gets messy quick.
I just automate the whole thing now. Set up a workflow that handles scrolling, waiting, and screenshots automatically. Works with any SPA and covers all the weird edge cases without writing custom Puppeteer code every time.
Saves me hours of debugging timing issues and actually works consistently. Check out Latenode for browser automation workflows like this: https://latenode.com
SPAs mess with viewport height because they calculate it dynamically after JavaScript runs. I’ve hit this tons of times with React and Vue apps. Here’s what actually works: force a document reflow before taking the screenshot. Drop await webPage.evaluate(() => document.body.scrollHeight) right before your screenshot call. This makes the browser calculate the real content height after everything’s loaded, which fullPage needs to work properly. Some SPAs also use CSS transforms or absolute positioning that throw off Puppeteer’s height calculations. Still getting cut off? Try await webPage.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) then await webPage.evaluate(() => window.scrollTo(0, 0)) before the screenshot. Forces everything to paint at least once.
try adding await webPage.evaluate(() => { document.body.style.height = 'auto'; }); before the screenshot. some SPAs set fixed heights that mess with puppeteer’s fullpage detection. also, check if your app uses lazy loading with intersection observer - it doesn’t always trigger properly with puppeteer’s default behavior.