If images look correct in Chromium but are missing, blank, or styled differently in a Puppeteer PDF, start by identifying what changed: print media rules, omitted CSS backgrounds, incomplete lazy loading, or an application that had not finished rendering. page.pdf() uses print CSS by default, and its printBackground option defaults to false. The fixes below isolate each cause without masking real load failures.
1. Classify the failure before changing code
Save a screenshot or inspect the page immediately before calling page.pdf(). Compare that state with the PDF and classify the missing visual:
- An
<img>or<picture>asset: check the image URL, loading state, responsive source selection, and lazy-loading trigger. - A CSS background: print backgrounds are disabled unless explicitly enabled.
- Image present, appearance wrong: print media queries or print color adjustment changed the result.
- Only images inserted by the app are absent: PDF generation started before the application reached its own ready state.
This distinction matters: printBackground can restore a background graphic, but it is not a universal fix for a missing <img>.
2. Understand Puppeteer’s PDF defaults
Print media is used by default
Page.pdf() generates the document with the print CSS media type. A stylesheet such as @media print { img { display:none } }, a print-only width rule, or a different src selected by a media query can therefore produce a PDF that does not match the browser view.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
If the PDF is intended to look like the screen, set screen media immediately before PDF generation:
await page.emulateMediaType('screen');
await page.pdf({ path: 'output.pdf', printBackground: true });
Use this only when screen styling is the desired output. If you are producing a print document, keep print media and correct the relevant print rules instead.
Background graphics are off unless enabled
The documented default for printBackground is false. Enable it when a visual is supplied by background-image, gradients, background colors, or another CSS background:
await page.pdf({
path: 'output.pdf',
printBackground: true
});
An ordinary image element still needs to load successfully; this option does not repair a broken URL, a failed request, or an image that was never inserted.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchRank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Fonts are not images
Puppeteer PDF generation waits for fonts by default (waitForFonts: true). In a background page, the documentation cautions that bringing the page to the front may be necessary for font loading to finish. That behavior does not mean images are ready. Do not use font readiness as evidence that image decoding or lazy loading has completed.
3. Wait for the page’s actual readiness
Use navigation lifecycle waits as a starting point
The official PDF guide demonstrates navigation with waitUntil: 'networkidle2'. Puppeteer defines networkidle2 as no more than two network connections for at least 500 milliseconds; networkidle0 uses zero connections for that minimum interval:
await page.goto(url, { waitUntil: 'networkidle2' });
You can also wait after navigation:
await page.waitForNetworkIdle({ idleTime: 500 });
waitForNetworkIdle() waits at least the configured idle time. These are synchronization points, not a guarantee that every framework task, lazy image, or deferred decode has completed. Analytics, polling, service workers, and long-lived connections can also make a strict idle condition unsuitable.
Wait for an application signal
Prefer a concrete readiness signal supplied by the page: a selector such as [data-render-complete="true"], a known hero image, or an application event exposed for automation.
Rank #3
await page.waitForSelector('[data-render-complete="true"]', {
timeout: 30000
});
If the site has no signal, wait for the actual image elements in page context. The following diagnostic helper checks completion and natural dimensions, then waits for load or error outcomes. Adapt it for your page’s <picture> sources and lazy-loading mechanism:
await page.evaluate(async () => {
const images = [...document.images];
await Promise.all(images.map(img => {
if (img.complete) return Promise.resolve();
return new Promise(resolve => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
});
}));
const failed = images.filter(img => !img.naturalWidth);
if (failed.length) {
throw new Error(`Images without usable pixels: ${failed.length}`);
}
});
img.complete only says that loading finished (successfully or unsuccessfully); naturalWidth helps distinguish a usable decoded resource from a failed one. For lazy images, scroll or trigger the component’s documented load action before this check. For CSS backgrounds, inspect the computed style and wait for the page’s own render-complete condition.
4. A complete Puppeteer pattern
This example combines the diagnostics without assuming that network idle alone is sufficient. Replace the URL and readiness selector with values from your application.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com/report', {
waitUntil: 'networkidle2',
timeout: 60000
});
// Use this when the PDF should match screen media.
await page.emulateMediaType('screen');
// Prefer your app's signal over a generic delay.
await page.waitForSelector('[data-render-complete="true"]', {
timeout: 30000
});
await page.evaluate(async () => {
const images = [...document.images];
await Promise.all(images.map(img => {
if (img.complete) return Promise.resolve();
return new Promise(resolve => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
});
}));
const failed = images.filter(img => !img.naturalWidth);
if (failed.length) throw new Error(`Failed images: ${failed.length}`);
});
await page.pdf({
path: 'report.pdf',
format: 'A4',
printBackground: true,
waitForFonts: true,
preferCSSPageSize: true
});
} finally {
await browser.close();
}
If print styling is intentional, remove emulateMediaType('screen') and fix the print stylesheet. If the page has no readiness selector, use a targeted predicate or a short, justified delay after triggering lazy loading; avoid an arbitrary long sleep as your only synchronization mechanism.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
5. Troubleshooting by symptom
The PDF has no colored panels or decorative graphics
Those are often CSS backgrounds. Set printBackground: true. Also check whether print CSS deliberately removes them.
An <img> is present in the DOM but blank
Inspect its src/currentSrc, request status, and naturalWidth. A relative URL may resolve against an unexpected base, an authenticated endpoint may reject Chromium, or a responsive image may select an unavailable source. Fix the request or credentials, then wait for load before calling pdf().
Only images below the fold are missing
The page likely uses lazy loading. Trigger the component’s loading behavior (often by scrolling through the document), wait for its completion signal, and then verify each relevant image. Network idle can occur before an intersection observer has requested those assets.
The browser screenshot is correct but the PDF is not
Compare media modes first. If screen appearance is required, call page.emulateMediaType('screen'). Otherwise inspect @media print rules and print-specific dimensions.
Best Value
Colors look washed out or different
Print rendering can modify colors. When exact colors are required, use CSS -webkit-print-color-adjust: exact; on the relevant elements or document, while understanding that color management and printer-oriented styling still apply.
Network-idle waiting hangs or finishes too soon
Use networkidle2 for pages with a few persistent connections, or a targeted readiness selector. Do not assume networkidle0 is practical for applications with polling or streaming. Conversely, a page can reach either idle state while a framework still schedules image work, so combine lifecycle waiting with an app-specific check.
Images work locally but fail in production
Check deployment-specific URLs, certificates, authentication headers, cookies, user-agent behavior, content-security policies, and the Chromium revision used by the deployed Puppeteer version. Log failed image URLs and response statuses from the page rather than treating the PDF as the first diagnostic surface.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.6. A practical diagnostic checklist
- Reproduce the PDF and inspect the page immediately before
page.pdf(). - Decide whether the missing visual is an image element, a CSS background, or print-only styling.
- Enable
printBackgroundfor backgrounds. - Choose print or screen media deliberately.
- Wait for navigation activity, then wait for the application’s render-complete condition.
- Check image
complete,naturalWidth, selected source, and failed requests. - Trigger lazy loading and verify below-the-fold assets.
- Account for print color adjustment when pixels exist but colors differ.
- Record the Puppeteer version, browser revision, URL, viewport, and relevant headers so a production failure can be reproduced.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server when you need a rendered capture without maintaining Puppeteer. One GET request returns PNG, JPEG, WebP, or PDF:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemscurl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for request options. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots, and every feature is included on every plan. Create a free ScreenshotNeo account.
7. Other clients for the same endpoint
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
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}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', data));
FAQ
Does waitForFonts wait for images?
No. It concerns font loading; image readiness requires its own checks.
Should I always use networkidle0?
No. Select a lifecycle wait that fits the site’s connections and add an application-specific readiness condition.
Is printBackground required for every image?
No. It targets CSS background graphics. An <img> must still load and decode successfully.
Free tools Windows power users keep installed
One-click scans. No signup required.
Quick Recap
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.

