How to wait for web fonts before taking a Puppeteer screenshot
Wait for document.fonts.ready after navigation and before page.screenshot() so the capture uses the page’s loaded fonts.
After navigating to a page—or after adding content that uses web fonts—await document.fonts.ready in the page, then call page.screenshot(). This explicitly waits for the document’s font-loading work to finish:
await page.goto(url, { waitUntil: 'networkidle2' });
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ path: 'page.png' });
networkidle2 is a navigation lifecycle condition, not a documented guarantee that the fonts needed for your screenshot are ready. Treat navigation and font readiness as separate waits. See Puppeteer’s screenshot guide and PDFOptions reference.
1. A complete Puppeteer example
Install Puppeteer in a Node.js project, save this as screenshot.js, and run it with node screenshot.js https://example.com. The script validates its URL argument, waits for navigation and fonts, and writes a PNG:
const puppeteer = require('puppeteer');
async function main() {
const url = process.argv[2];
if (!url) {
throw new Error('Usage: node screenshot.js <url>');
}
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
document.fonts is the page’s FontFaceSet. Its ready promise resolves when the document’s font loading and related layout work are complete. Awaiting it in page.evaluate() makes the browser-page promise the gate before the Node.js code proceeds to the screenshot.
2. Why network idle is not enough
Puppeteer’s page.goto() accepts a waitUntil option that determines which navigation lifecycle event to await. The screenshot guide demonstrates networkidle2, but that event does not itself express the condition “the fonts used by this page are ready.” For a capture that depends on typography, wait for the font condition explicitly after navigation.
The order matters:
- Navigate to the page and wait for the lifecycle condition appropriate to your site.
- If the page adds font-using content after navigation, add that content first.
- Await
document.fonts.ready. - Capture with
page.screenshot().
3. Alternative: wait for the font status
You can express the wait as a page condition with Puppeteer’s page.waitForFunction():
await page.goto(url, { waitUntil: 'networkidle2' });
await page.waitForFunction(() => document.fonts.status === 'loaded');
await page.screenshot({ path: 'page.png' });
waitForFunction() waits until a function evaluated in the page context returns a truthy value. The promise form using document.fonts.ready is the most direct font-specific wait; the status form can be useful when you want the condition to be visible in the code. See Puppeteer’s waitForFunction reference.
4. Handle content added after navigation
A navigation wait only covers navigation. If your application renders a chart, route, modal, or other font-using content afterward, wait for that content to exist before waiting for fonts:
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('#report');
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ path: 'report.png' });
For a page whose own script inserts content later, the same rule applies: wait for the relevant application state, then await the font set, then capture. This avoids treating initial document readiness as proof that later content and its typography have settled.
5. Screenshot options and PDF distinction
Use page.screenshot() for an image capture. Its documented screenshot options include choices such as output type, full-page capture, clipping, and capture behavior; consult the Page.screenshot reference and ScreenshotOptions interface for the version you have installed.
Puppeteer’s PDF API is different: its PDFOptions includes waitForFonts, which defaults to true and waits for document.fonts.ready. The documentation notes that bringing a background page to the front may be required. That is a PDF setting; the cited screenshot API references do not document a corresponding waitForFonts screenshot option. For screenshots, await font readiness yourself.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The screenshot uses fallback typography. | The screenshot ran before the page’s font set was ready, or the font failed to load. | Await document.fonts.ready before capture, then inspect the page’s font requests and browser console for load errors. |
networkidle2 completed, but the font is still missing. |
Network idle is a navigation lifecycle condition, not an explicit font-readiness test. | Add a separate page.evaluate(() => document.fonts.ready) wait. |
| The page looks right initially, but a later component is misaligned. | The component was inserted after the font wait or after the screenshot. | Wait for the relevant selector or application state, then await document.fonts.ready again before capture. |
| The wait appears stuck or eventually times out. | A font request may be slow, blocked, or failing; a page condition may also never become true. | Check the font URL, network access, CORS configuration, and console errors. Set a suitable timeout on Puppeteer waits and handle timeout errors in the calling script. |
You expected a waitForFonts option on page.screenshot(). |
The documented setting is on PDFOptions, not the screenshot options referenced here. | Await the page’s font promise yourself before calling page.screenshot(). |
A font readiness wait cannot make an unavailable font load successfully. It ensures the capture waits for the browser’s font-loading work to settle; verify the actual font loaded when the exact typeface matters.
7. Performance and reliability
Waiting for document.fonts.ready adds time only when font work is still pending. Avoid replacing it with an arbitrary fixed sleep: a short sleep can finish before a slow font, while a long one adds delay when fonts were already ready. A navigation timeout and a font or selector wait address different stages, so choose timeouts that reflect your own page and report failures instead of silently capturing a fallback state.
For repeatable captures, make the page state deterministic: wait for the target content, wait for fonts, and then capture. When a screenshot is part of an automated job, close the browser in a finally block as in the example so errors do not leave the browser process running. Puppeteer’s APIs and option details can change; check the documentation for the version installed by your project.
8. Or skip the browser setup
ScreenshotNeo provides a website screenshot API: one GET request returns a PNG, JPEG, WebP, or PDF. See the API documentation for available parameters. 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://stripe.com -o shot.webp
Equivalent Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Equivalent Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo accepts cookie and 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, blank pages, and failed loads are never billed. Its MCP server lets AI agents use screenshot tools. The Free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 shots. Sign up for 1,000 free screenshots a month, with no card required.
9. FAQ
Does Puppeteer have a screenshot option called waitForFonts?
The cited screenshot options do not document one. The PDF options do include waitForFonts; for screenshots, explicitly await document.fonts.ready.
Should I use networkidle0 or networkidle2?
Choose the navigation condition that suits the page, then add the explicit font wait when typography matters. Neither lifecycle choice replaces that font-specific condition.
What if the page has no web fonts?
The font readiness promise still provides a clear synchronization point and resolves when the document’s font loading work is complete.
Can I use the same wait before generating a PDF?
Yes, though Puppeteer’s PDF options already document waitForFonts, enabled by default. Check the PDFOptions reference for details.


