I’m trying to make PDFs from a single-page app using Puppeteer. The tricky part is waiting for everything to load properly. I’ve tried a bunch of stuff, but nothing seems to work right.
Here’s what my code looks like now:
const browser = await puppeteer.launch({
executablePath: 'C:\Chrome\chrome.exe',
headless: true,
args: ['--no-sandbox']
});
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
await page.type('#user', 'admin');
await page.type('#pass', 'secret');
await page.click('#SignIn');
// This feels hacky
await page.waitFor(2000);
await page.pdf({
path: 'output.pdf',
format: 'A4'
});
The problem is I don’t want to use that waitFor(2000)
. It’s not reliable. The page has charts and stuff that load after some calculations, so I can’t use waitForSelector
either.
Any ideas on how to wait until everything’s actually loaded before making the PDF? Thanks for any help!
hey man, i feel ur pain with puppeteer. one thing that worked for me was using page.waitForFunction() to check for a specific condition on the page. like maybe wait for a certain element to appear or for a global variable to be set. it’s more flexible than waitForSelector. here’s a quick example:
await page.waitForFunction(() => {
return document.querySelector(‘.chart-loaded’) !== null;
});
hope that helps!
I’ve encountered similar issues with Puppeteer and SPA content loading. One approach that’s worked well for me is combining multiple wait strategies. Try this:
- Use
waitUntil: 'networkidle0'
instead of networkidle2
for stricter network idle.
- Implement a custom function to check for specific page elements or state changes.
- Set a reasonable timeout as a fallback.
Here’s a rough example:
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
// After login
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle0' }),
page.click('#SignIn')
]);
await page.waitForFunction(() => {
// Check for key elements or a global variable indicating app readiness
return document.querySelector('.chart-container') !== null && window.appLoaded === true;
}, { timeout: 10000 });
// Now generate PDF
This method has proven more reliable by adjusting to varying load times without relying on fixed delays.
Hey there, I’ve been in your shoes with Puppeteer and SPAs. One trick that’s worked wonders for me is using a custom Promise that resolves when your app is truly ready. Here’s what I mean:
await page.evaluate(() => {
return new Promise((resolve) => {
if (window.myAppIsReady) {
resolve();
} else {
window.addEventListener('myAppReady', resolve, { once: true });
}
});
});
You’d need to set window.myAppIsReady = true
or dispatch a ‘myAppReady’ event in your SPA when everything’s loaded. This way, Puppeteer waits for your app to signal it’s good to go.
It’s been rock-solid for me, way more reliable than fixed timeouts or selectors. Give it a shot and let me know how it goes!