Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
JavaScript

How to Wait for All Images to Load Before Taking a Puppeteer Screenshot

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Before calling page.screenshot(), wait for the page’s current <img> elements to finish decoding, then check that each has a usable image width. A navigation or network-idle wait alone does not prove that images are decoded and ready to paint.

Wait for current images to decode before capture

page.evaluate() can return a promise; Puppeteer waits for that promise to resolve before continuing. Use the browser’s HTMLImageElement.decode() method for each image currently in the document, and verify naturalWidth so a failed image is not mistaken for a successful load. Puppeteer’s screenshot guide demonstrates this pattern.

await page.evaluate(async () => {
  await document.fonts.ready;

  await Promise.all(
    Array.from(document.images, async (image) => {
      await image.decode();
      if (!image.naturalWidth) {
        throw new Error(`Broken image: ${image.src}`);
      }
    }),
  );
});

await page.screenshot({ path: 'page.png' });

The font wait is useful when text appearance matters too; it is separate from image readiness. The image loop rejects if decoding fails. Decide explicitly whether that should abort the screenshot or whether the job should continue while reporting the missing asset.

Use a bounded wait and useful failure details

A page can contain an image that never becomes usable, so production jobs should have a deadline rather than wait forever. Puppeteer’s cited example does not prescribe a universal timeout or failure policy. The wrapper below bounds the readiness check and labels the failure. Its timeout bounds the wait in Node; if it expires, the page-side evaluation may still be running until the page or browser is closed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const imageTimeoutMs = 20_000;

async function waitForCurrentImages(page) {
  let timer;
  try {
    await Promise.race([
      page.evaluate(async () => {
        await document.fonts.ready;
        const images = Array.from(document.images);
        const results = await Promise.allSettled(
          images.map(async (image) => {
            await image.decode();
            if (!image.naturalWidth) {
              throw new Error(`No natural width: ${image.currentSrc || image.src}`);
            }
            return image.currentSrc || image.src;
          }),
        );
        const failures = results
          .map((result, index) => ({ result, image: images[index] }))
          .filter(({ result }) => result.status === 'rejected')
          .map(({ result, image }) => ({
            url: image.currentSrc || image.src,
            error: String(result.reason),
          }));
        if (failures.length) {
          throw new Error(`Images not ready: ${JSON.stringify(failures)}`);
        }
      }),
      new Promise((_, reject) => {
        timer = setTimeout(
          () => reject(new Error(`Image readiness exceeded ${imageTimeoutMs} ms`)),
          imageTimeoutMs,
        );
      }),
    ]);
  } finally {
    clearTimeout(timer);
  }
}

await waitForCurrentImages(page);
await page.screenshot({ path: 'page.png' });

Promise.allSettled() lets this version collect the URLs of failed images rather than stopping at the first rejection. If your policy is to tolerate failures, change the failure branch to log the array and continue; make that outcome visible to the calling job rather than treating the image set as complete.

Why network idle is not enough

waitForNetworkIdle() is a network-activity condition, while image decoding is a browser rendering operation. A request can have finished while its image has not yet been decoded for display. Use network-idle or a navigation wait to help establish that the page has settled, then run the explicit image check if the screenshot depends on decoded image content. See Puppeteer’s Page API for evaluate(), waitForFunction(), and waitForNetworkIdle().

await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await waitForCurrentImages(page);
await page.screenshot({ path: 'page.png' });

Navigation conditions and network-idle modes are not substitutes for checking what the page needs to render. Pages with polling, streaming, or other persistent requests may also make a network-idle strategy unsuitable; choose the navigation condition for the site, then use the image-specific readiness check.

Handle lazy-loaded and dynamically inserted images

The loop examines document.images at the moment it runs. It does not cover images inserted later or CSS background images. Lazy-loaded content may not even request its images until it approaches the viewport. Trigger the content you intend to capture before taking the snapshot, wait for insertion, and then run the decode check.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Scroll to trigger lazy loading

For a page that loads content as the visitor scrolls, move through the relevant page area first. A simple full-height scroll can trigger many viewport-based loaders, but it is not a guarantee: some sites use custom conditions, virtualized lists, or a separate “load more” control.

await page.evaluate(async () => {
  const step = Math.max(window.innerHeight, 1);
  for (let y = 0; y < document.body.scrollHeight; y += step) {
    window.scrollTo(0, y);
    await new Promise((resolve) => setTimeout(resolve, 100));
  }
  window.scrollTo(0, 0);
});

await waitForCurrentImages(page);
await page.screenshot({ path: 'page.png', fullPage: true });

The short pause is an example, not a universal load duration. Increase or replace it with a page-specific condition when the site needs more time to fetch or insert content. For a predictable page, prefer waiting for a relevant selector or application-specific signal.

Wait for later insertions

If the page adds images asynchronously, wait for a meaningful condition before enumerating images. Puppeteer’s waitForFunction() can wait for a browser-side predicate, but the predicate must match the page’s behavior. For example, if a known gallery container receives a minimum number of images, wait for that count, then run the decode check.

await page.waitForFunction(() => {
  const gallery = document.querySelector('[data-gallery]');
  return gallery && gallery.querySelectorAll('img').length >= 8;
});
await waitForCurrentImages(page);

Replace the selector and expected count with values that represent the content you actually need. A count alone does not ensure those images decoded; it is only the gate before the decode check.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

CSS background images need separate handling

document.images contains image elements, not CSS backgrounds. If a critical visual is supplied by background-image, the image loop will not check it. Identify the relevant element and URL from computed styles, then load and decode that URL with an Image object in the page context, or wait for a page-specific readiness signal. Backgrounds may also change across media queries or state changes, so check the state that will be captured.

Choose failure behavior deliberately

A broken or inaccessible image is not “loaded successfully.” decode() can reject, and naturalWidth of zero is a practical failure check used in Puppeteer’s guidance. Your automation should report the affected URL and make a clear policy choice.

  • Fail the capture: Use when a complete visual record is required, such as a compliance or visual-regression snapshot.
  • Capture with a warning: Use when partial content is acceptable; return the missing URLs with the screenshot result.
  • Retry selectively: Use when transient delivery failures are plausible, but keep retries bounded and avoid retrying permanent failures indefinitely.

For diagnostics, record currentSrc as well as src; browsers can choose a responsive candidate from srcset, so currentSrc identifies the selected resource when available.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Full-page capture is separate from image readiness

page.screenshot({ fullPage: true }) changes the capture extent to the full page. It does not establish that image assets were decoded. Puppeteer documents fullPage as defaulting to false in its screenshot options. Prepare the content and images independently, then choose viewport or full-page capture.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
The SQL Programming Language: .
  • Used Book in Good Condition
await waitForCurrentImages(page);
await page.screenshot({ path: 'full-page.png', fullPage: true });

If the page uses lazy loading below the initial viewport, trigger those regions before the check; otherwise the check may only see the images already present or requested. Also consider whether scrolling changes sticky elements or page state before capture.

Common problems and fixes

  • The screenshot still omits images after network idle: Add the decode() check before capture; network activity and image decode are different readiness signals.
  • The image loop misses content lower on the page: Trigger the lazy-loaded regions and wait for their insertion before enumerating images.
  • The wait rejects: Inspect the reported URL and error. Decide whether to fail, retry within a limit, or capture with an explicit missing-image warning.
  • The check passes but a background is absent: CSS backgrounds are not in document.images; check the relevant background resource separately.
  • The operation takes too long: Set a deadline, capture diagnostics, and investigate the page’s slow or stalled resources instead of allowing an indefinite wait.
  • Full-page output is still incomplete: fullPage controls extent only. Trigger lazy content and verify readiness before taking the full-page screenshot.

Or skip the browser setup

If you need an image or PDF from a URL without maintaining Puppeteer navigation and readiness code, ScreenshotNeo is a website screenshot API and MCP server. Its full-page capture option loads lazy images. Before a capture, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response reports the page verdict and billing status in headers.

One GET request returns an image or PDF. See the ScreenshotNeo API documentation for parameters and output options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000 shots. Sign up for the free plan.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does the image decode check wait for CSS background images?

No. It checks current image elements in the document. Background resources need a separate readiness check.

Does Puppeteer’s fullPage option wait for images?

No. It changes the captured page extent; image readiness must be handled separately.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.