How to Capture a Web Page Screenshot After Web Fonts Finish Loading
Wait for page content, await document.fonts.ready, then capture. See runnable Playwright and Puppeteer examples, font checks, and troubleshooting.
Direct answer: after navigating to the page and waiting for the content you need, run await document.fonts.ready inside the page, then take the screenshot. Navigation completion and font readiness are separate waits. If a particular font face matters to the result, also check that face or inspect the rendered page: the readiness promise alone does not prove that every declared font loaded successfully.
This guide shows the sequence in Playwright and Puppeteer, explains viewport, full-page, and element captures, and covers failures that can make font-sensitive screenshots unreliable.
1. Why wait for fonts separately?
A navigation wait tells your automation when a navigation condition has been met. It does not, by itself, establish that the web fonts used by the visible content have finished loading. If capture happens too early, the screenshot can show fallback typography, different line wrapping, or a layout that changes after capture.
document.fonts.ready is a promise you can await in the page context before capture. Treat it as a practical synchronization point for the document’s font work, not as proof that the preferred font file was fetched and used. A page can finish its font loading work while a specific face is unavailable or a CSS rule falls back to another face. Check the font that matters when typography fidelity is a requirement.
For repeatable visual comparisons, font readiness is only one part of the setup. Dynamic data, animations, image loading, responsive layout, and other changing page state can also alter pixels.
2. Playwright: wait, then capture
Install Playwright and its browser as described in the official Playwright getting started guide. Save this as screenshot.mjs and run it with Node.js. Replace the URL and, if needed, the content selector.
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
try {
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('main').waitFor({ state: 'visible' });
// Wait for the document's font loading work before capturing.
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ path: 'page.png' });
} finally {
await browser.close();
}
The example waits for a page-specific content landmark rather than assuming that navigation alone means the content is ready. If the target has no main element, choose a selector that identifies the content you intend to capture or remove that wait if it is not needed.
Playwright documents viewport screenshots, full-page capture, image type, scale, timeout, and other options in its Page API. For a full-page image, use fullPage: true. For a CSS-pixel-sized output, use scale: 'css'; otherwise select the scale appropriate to your desired output and device scale.
await page.screenshot({
path: 'full-page.png',
fullPage: true,
type: 'png',
scale: 'css',
timeout: 30_000
});
Use the options that match the capture. For example, a viewport capture is useful for a fixed screen composition; a full-page capture includes the scrollable page; and a locator screenshot targets one element. Wait for fonts before whichever capture method you choose.
3. Puppeteer: wait, then capture
Install Puppeteer using the official installation guide. Save this as screenshot.mjs and run it with Node.js.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
try {
await page.setViewport({ width: 1440, height: 1000 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.waitForSelector('main', { visible: true });
// Font readiness is an explicit wait, separate from navigation idleness.
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ path: 'page.png' });
} finally {
await browser.close();
}
Puppeteer’s screenshots guide demonstrates navigation with waitUntil: 'networkidle2' followed by a page screenshot. Network idleness can be a useful navigation condition, but keep the explicit font wait when font timing affects the result. Puppeteer also supports element screenshots through an element handle:
const heading = await page.waitForSelector('h1', { visible: true });
await page.evaluate(() => document.fonts.ready);
await heading.screenshot({ path: 'heading.png' });
See the Puppeteer screenshots guide for page and element capture behavior.
4. Verify the font when exact typography matters
A readiness wait does not confirm that a particular family, weight, or style rendered. First make the check specific to the page: identify the element and the expected font family and weight from its styles, then confirm the browser reports that face as available or use a controlled visual check appropriate to your page.
The CSS Font Loading API exposes a font set and font-face loading states. For a known family and sample string, a basic diagnostic can ask whether the browser considers that font available:
const fontCheck = await page.evaluate(() => {
const sample = document.querySelector('h1');
if (!sample) return { found: false };
const style = getComputedStyle(sample);
const shorthand = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
return {
found: true,
fontFamily: style.fontFamily,
fontWeight: style.fontWeight,
check: document.fonts.check(shorthand, sample.textContent || 'Aa')
};
});
console.log(fontCheck);
Use this as a diagnostic, not a universal proof that the exact intended file rendered: CSS font fallback, family naming, weight mapping, and browser behavior affect the interpretation. For a strict visual test, use a page-specific assertion or compare a controlled capture where the font asset and environment are known.
5. Choosing the capture type and output
| Need | Capture choice | Things to check |
|---|---|---|
| What a user sees in one screen | Viewport screenshot | Set viewport dimensions and device scale consistently. |
| The entire scrollable document | Full-page screenshot | Some pages lazy-load content while scrolling; ensure required content has appeared before capture. |
| One card, chart, or component | Element or locator screenshot | Wait for that element to be visible and for its fonts to be ready. |
| Stable visual regression output | Use fixed viewport and controlled page state | Also account for animation, timestamps, dynamic data, and resource changes. |
Playwright supports screenshot scale settings, including CSS-pixel output and device-scale output. Pick intentionally: device-pixel output can be larger and reflect a high-density viewport, while CSS scale is useful when comparisons should map one output pixel to one CSS pixel. Keep viewport, browser engine, and scale consistent across runs.
6. Reliability, speed, and cost considerations
- Wait narrowly: wait for the content needed by the capture, then fonts. Avoid using a long fixed sleep as a substitute for a state-based check; a sleep can be too short on a slow run and unnecessarily long on a fast one.
- Do not confuse network idle with font readiness: use network idle only when it suits the page’s request behavior, and retain the explicit font wait for font-sensitive captures.
- Bound waits: set appropriate navigation and screenshot timeouts, and log which step failed. A page that continually polls or holds connections open may not reach a network-idle condition even though its visible content is ready.
- Control visual state: fix viewport and browser versions for comparisons. Disable or handle animations when appropriate; Playwright screenshot assertions document waiting for matching consecutive screenshots and animation handling.
- Plan for huge pages: full-page images can consume more memory and take longer than viewport captures. Capture only the region needed where possible.
- Cost: local browser automation uses your own compute and browser infrastructure. Its operational cost depends on your runtime, concurrency, retries, and output storage; the cited framework documentation does not establish a universal cost per screenshot.
Playwright’s PageAssertions API describes screenshot assertions that wait for consecutive screenshots to match and can handle animations. That can improve visual comparison stability, but it is separate from confirming that a particular font face loaded.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Fallback font appears in the image | Screenshot ran before font loading and associated layout work settled, or the preferred face could not load. | Await document.fonts.ready after the relevant content is present. Then inspect the expected family, weight, and font loading outcome. |
| Text wraps differently between runs | Font availability, viewport, device scale, content, or dynamic page state differs. | Keep the browser, viewport, scale, content state, and font resources consistent; add a page-specific font check. |
networkidle never completes |
The page may maintain connections or make ongoing requests. | Use a suitable navigation milestone such as DOM content loaded, wait for a relevant content selector, and then explicitly await fonts. |
| Font readiness or screenshot wait times out | A browser, engine, page, or environment-specific issue may be involved; the page may also have unusual font behavior. | Record browser and automation versions, inspect font-set and font-face states, review timeout logs, and reproduce with a minimal page. Adjust timeout only after identifying the slow step. |
| The page screenshot is correct, but an element capture is blank or incomplete | The element was not visible or ready when captured, or it is outside the expected layout state. | Wait for the target element to be visible, await fonts, and capture that element after page-specific content settles. |
A Playwright issue report opened on September 29, 2026 describes one Linux WebKit 26.6 / Playwright 1.63.0 report where screenshot capture timed out while waiting for fonts; the report compared it with a reproduction on Playwright 1.60.0. It is a narrow, version- and environment-specific report, not evidence that font waits generally hang. Check versions and gather a controlled reproduction before drawing a broader conclusion: Playwright issue #42986.
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request can return an image or PDF, and its API accepts common screenshot parameter names. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners are accepted and removed before the shot; newsletter popups and chat widgets are removed too. Each step can be turned off.
- Bot checks and CAPTCHAs, blank pages, failed loads, timeouts, and cache hits are never billed. Response headers say the page verdict and billing status.
- An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
- The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots.
Explore ScreenshotNeo and read the API docs. Sign up for 1,000 free screenshots a month, with no card required.
9. FAQ
Does awaiting fonts guarantee the preferred font is visible?
No. It is a useful readiness wait, but verify the specific face or rendered result when exact typography matters.
Should I use Playwright or Puppeteer?
Use the framework already present in your automation stack. Both can wait in the page context and capture a page screenshot; the source material establishes no overall framework winner.
Can I use the same wait for a PDF?
Yes. Await font readiness before invoking the PDF capture as well when the document’s typography affects the output.
Is a fixed delay enough?
It can be a page-specific workaround, but it is less reliable than waiting for the relevant content and font readiness state.


