iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Use Puppeteer’s page.screenshot() method. Pass a path such as screenshot.png to write an image file, add fullPage: true for the complete document, or capture one element with an element handle. Without path, Puppeteer returns image bytes instead of creating a file.
Save a basic screenshot to a file
Install Puppeteer in a Node.js project, then launch a browser, open a page, capture it, and close the browser. This complete example writes screenshot.png:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' });
await browser.close();
If your project does not already use ECMAScript modules, either set "type": "module" in package.json or use a CommonJS import style supported by your Node.js setup. The path is resolved relative to the process’s current working directory unless you provide an absolute path.
Choose what Puppeteer captures
Capture the visible viewport
page.screenshot() captures the page area currently represented by the viewport. Set the viewport before navigation when a particular width, height, or responsive breakpoint matters:
#1 Best Overall
await page.setViewport({ width: 1440, height: 900 });
await page.goto('https://example.com');
await page.screenshot({ path: 'viewport.png' });
The screenshot reflects the browser’s rendered state at the moment the method runs. If content is still loading or an animation is in progress, wait for the condition your page requires before calling the method.
Capture the entire page
Set fullPage: true to capture the full document instead of only the viewport:
await page.goto('https://example.com');
await page.screenshot({
path: 'full-page.png',
fullPage: true
});
Very long pages produce correspondingly large images. If a page uses lazy-loaded images, make sure those images have been triggered and rendered before capture; otherwise the screenshot can contain placeholders or unloaded regions.
Capture a rectangular region with clip
A clip rectangle lets you select a region by coordinates. Supply its position and dimensions in CSS pixels:
await page.screenshot({
path: 'region.png',
clip: { x: 100, y: 200, width: 800, height: 500 }
});
The clip interface is based on a bounding box and has a scale default of 1. Keep the rectangle inside the rendered page area and use numbers that match the viewport and device scale you selected.
Capture one element
For a component such as a card, chart, or navigation bar, locate the element and call its screenshot method:
Rank #2
const card = await page.waitForSelector('.pricing-card');
if (!card) throw new Error('Pricing card was not found');
await card.screenshot({ path: 'pricing-card.png' });
Puppeteer scrolls the element into view when necessary. The call throws if the element has been detached from the DOM, so obtain a fresh handle after page updates rather than reusing a stale handle.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated 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 matchControl image format, quality, and transparency
PNG, JPEG, and WebP output
PNG is the documented default. When you provide a path, Puppeteer infers the file type from its extension. You can also set the type explicitly when the API mode you use supports that option:
await page.screenshot({ path: 'page.jpg', type: 'jpeg', quality: 85 });
await page.screenshot({ path: 'page.webp', type: 'webp', quality: 80 });
quality accepts values from 0 through 100 and applies to JPEG and WebP. It does not apply to PNG, so changing quality will not reduce a PNG’s size or alter its output.
Transparent backgrounds
Use omitBackground: true to hide the default white page background and preserve transparency where the rendered content permits it:
await page.screenshot({
path: 'transparent.png',
omitBackground: true
});
Transparency is most useful for isolated graphics or overlays. A page whose CSS paints an opaque background will still contain that painted color.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesSave bytes in memory instead of writing a file
The path option is optional. Without it, the screenshot method returns image data as a Uint8Array, which is useful when you want to upload the result, attach it to a response, or process it without a temporary file:
const imageBytes = await page.screenshot();
// Example: write the returned bytes yourself
import { writeFile } from 'node:fs/promises';
await writeFile('memory-result.png', imageBytes);
Request base64 text with encoding: 'base64':
const base64Image = await page.screenshot({ encoding: 'base64' });
console.log(base64Image.slice(0, 40));
Use bytes for binary storage and base64 when an API or JSON payload specifically requires text. Base64 increases the payload size, so it is usually less efficient for large screenshots.
Make captures deterministic
A screenshot is a snapshot of the page’s current render, not a semantic export. Navigate first, then wait for the page state your test or job requires. For a selector-dependent page, wait for that selector before capturing:
await page.goto('https://example.com/dashboard');
await page.waitForSelector('[data-ready="true"]');
await page.screenshot({ path: 'dashboard.png', fullPage: true });
For pages with delayed data, fonts, animations, or lazy images, define a reliable readiness condition in the application or test. Avoid using an arbitrary delay as the only synchronization mechanism when a concrete selector or application state is available. If you must use a delay for a known animation, keep it explicit and as short as the page permits.
Set a viewport deliberately when responsive layout matters. A different viewport can change navigation, line wrapping, lazy-loading thresholds, and the resulting image dimensions.
Where the screenshot file goes
A relative path such as screenshots/home.png is relative to the process’s current working directory, usually the directory from which you ran the Node command—not necessarily the directory containing the JavaScript file. Create the directory first when it may not exist:
import { mkdir } from 'node:fs/promises';
await mkdir('screenshots', { recursive: true });
await page.screenshot({ path: 'screenshots/home.png' });
Use an absolute path when a job runner, container, or service starts in an unpredictable directory. Log the resolved path so a CI failure does not look like a missing screenshot when the file was written somewhere else.
Rank #4
WebDriver BiDi compatibility
Puppeteer’s WebDriver BiDi support documentation currently lists only clip, encoding, and fullPage among the supported Page.screenshot() parameters. Do not assume that every option in the general ScreenshotOptions interface works when you connect through BiDi; in particular, verify support before relying on path, type, or other connection-specific options. If an option is rejected, capture bytes using the supported settings and write or convert them in your Node.js process.
Free tools Windows power users keep installed
One-click scans. No signup required.
Troubleshoot common failures
No image file appears
- Cause: You omitted
path, so Puppeteer returned bytes instead of writing a file. - Fix: Add a path, or persist the returned
Uint8Arraywithfs/promises.writeFile.
The file is in an unexpected directory
- Cause: Relative paths use the process working directory.
- Fix: Print
process.cwd(), run the command from the intended directory, or pass an absolute path.
The screenshot is only the top portion
- Cause: Viewport capture is the default.
- Fix: Set
fullPage: true, or use an element screenshot or clip rectangle when you need only a defined area.
An element screenshot throws a detached-node error
- Cause: The framework replaced the element after you obtained its handle.
- Fix: Wait for the final render and query the element again immediately before
elementHandle.screenshot().
JPEG quality changes nothing
- Cause: Quality does not apply to PNG.
- Fix: Use a JPEG or WebP path/type when lossy compression is appropriate, then set a quality from 0 to 100.
BiDi rejects a screenshot option
- Cause: BiDi currently documents a narrower option set than the general API.
- Fix: Check the current BiDi support documentation, reduce the call to supported parameters, and handle file writing or format conversion in your process.
The page looks incomplete
- Cause: The capture ran before asynchronous content, fonts, animations, or lazy images finished rendering.
- Fix: Wait for a page-specific readiness selector or state, then capture. For a full-page image, verify that below-the-fold content has actually loaded.
Performance, reliability, and cost considerations
Launching a browser is expensive compared with taking another screenshot from an already-open page. For a batch job, reuse one browser and, where isolation allows, create pages for individual URLs; always close pages and the browser in a finally block so failures do not leave processes running. Full-page images consume more memory and disk than viewport captures, and base64 adds transport overhead. Choose the smallest capture scope and format that meets the requirement.
For repeatable visual tests, pin the viewport, browser environment, fonts, and page readiness condition. Record the URL, output path, and capture settings with the artifact. Treat screenshots as build outputs: write to a known directory, check that the file exists and has nonzero size, and surface navigation or rendering errors rather than silently accepting a blank image.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo is a hosted screenshot API and MCP server. It is the first alternative to try when you do not want to maintain Chromium setup: it removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; and the lowest paid plan starts at $5 for 3,000 shots. Every response identifies the page verdict and billing status with X-Page-Verdict and X-Billed headers.
Make one GET request. The API returns PNG, JPEG, WebP, or a PDF depending on your parameters. See the ScreenshotNeo API documentation for the complete parameter reference.
Recommended Free Tools
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,
)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
ScreenshotNeo includes full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, pre-capture clicks, hidden selectors, selector/delay/network-idle waits, ad and tracker blocking, custom headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, an OpenAPI specification, and compatibility with parameter names used by other screenshot APIs. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
| Plan | Included shots | Price |
|---|---|---|
| Free | 1,000 per month | $0, no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
Yearly billing provides two months free, and every feature is available on every plan. Start with 1,000 free screenshots a month—no card required.
FAQ
Can Puppeteer save a screenshot as a PDF?
The workflow here is for image screenshots. Use Puppeteer’s PDF workflow separately when the required artifact is a paginated document rather than a PNG, JPEG, or WebP image.
Does fullPage change the browser viewport?
No. It changes the captured document area; set the viewport separately when you need a specific responsive layout.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Can I use an element handle after navigation?
No. Navigation commonly replaces the document. Query the element after navigation and after the final render state is reached.
What should I check before upgrading Puppeteer?
Recheck the current screenshot option reference and the connection-mode documentation, especially if you use WebDriver BiDi, because supported parameters can differ from the general interface.
Frequently Asked Questions
Can Puppeteer save a screenshot as a PDF?
The workflow here is for image screenshots. Use Puppeteer’s separate PDF workflow when you need a paginated document.
Does fullPage change the browser viewport?
No. It changes the captured document area; set the viewport separately for responsive-layout control.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Can I reuse an element handle after navigation?
No. Query the element again after navigation and final rendering because the original DOM node may have been replaced.
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.

