ScreenshotNeo

BlogHow-to

How to Capture a Full-Page Screenshot Using Puppeteer in Node.js

Capture an entire page with Puppeteer’s `fullPage: true`. Learn how to save or return the image, handle dynamic content, and troubleshoot common issues.

By the ScreenshotNeo team4 October 20267 min read

Use Puppeteer’s page.screenshot({ fullPage: true }) to capture the rendered page beyond the current viewport. Pass a path to save the image; without one, Puppeteer returns the image data instead of writing a file. The fullPage option defaults to false, so set it explicitly. See the Puppeteer screenshot guide and ScreenshotOptions API reference.

1. Capture and save a full-page screenshot

Install Puppeteer, then run this ES module. The package’s typical Node.js workflow imports Puppeteer, launches a browser, opens a page, and navigates to the target URL.

npm install puppeteer
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

Save this as screenshot.mjs and run node screenshot.mjs. Replace the URL with a page you are authorized to access. The finally block closes the browser even if navigation or capture fails. networkidle2 is one available navigation wait choice, as shown in Puppeteer’s guide; it is not a universal signal that every site has finished rendering.

What the key options do

  • fullPage: true requests a capture of the full page. If omitted, the default is false and the screenshot is viewport-sized.
  • path: 'page.png' writes the result to disk. Puppeteer infers the image type from the filename extension; the documented default type is PNG.
  • Without path, the screenshot is not written to disk. The call returns image bytes by default, which you can pass to another library or write to a file yourself.
  • type can select an output format. Supported screenshot output options include PNG, JPEG, and WebP; JPEG and WebP quality can be configured with quality, from 0 to 100. Quality does not apply to PNG. When relying on the filename extension to select the format, use an extension that matches the intended type.

2. Wait for the content you need

A full-page capture covers the page as rendered when the screenshot is taken. The fullPage setting does not promise that a site’s lazy images, infinite-scroll feed, animations, or application data have finished loading. Choose a readiness condition that matches the page.

await page.goto('https://example.com/products', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-page-ready="true"]');
await page.screenshot({ path: 'products.png', fullPage: true });

Use a selector your application adds when the relevant content is ready. For a page without such a marker, wait for a meaningful element or a known delay only when that fits the site. A fixed delay can be simple, but it may waste time or still be too short if the page is slow.

For lazy-loaded sections that appear only as they approach the viewport, scrolling through the page before capturing may prompt the site to load them. This is page-specific behavior, not a guarantee provided by fullPage. Infinite-scroll pages need a stopping rule: decide how many items or how much content to load, perform the required scrolling and waits, then capture. Otherwise, the page can keep growing or never reach a stable state.

3. Return image bytes or base64

When the screenshot is going to an upload, response, or image-processing step, omit path and use the returned bytes:

const imageBytes = await page.screenshot({ fullPage: true });
// imageBytes is a Uint8Array; pass it to your storage or image-processing code.

The documented base64 overload returns a base64 string:

const imageBase64 = await page.screenshot({ fullPage: true, encoding: 'base64' });

Base64 is convenient for embedding or text-based transport, but it expands binary data and can increase memory use. Prefer bytes for binary storage or transfer when your destination accepts them.

4. Configure the capture

These options are documented by Puppeteer’s ScreenshotOptions reference. Use only the options that match the output you need.

Option Use Notes
fullPage Capture the full page rather than only the viewport. Optional boolean; defaults to false.
path Save the image to disk. Type is inferred from the filename extension. Without a path the image is not saved to disk.
type Select PNG, JPEG, or WebP output. PNG is the documented default.
quality Set JPEG or WebP quality. Integer from 0 to 100; does not apply to PNG.
encoding Choose returned data encoding. 'base64' returns a base64 string; otherwise the API can return image bytes.
clip Capture a specified rectangle. Use when you need a region rather than an ordinary full-page capture.
captureBeyondViewport Control capture outside the viewport. The documented default is false when there is no clip and true when there is one.
omitBackground Omit the default page background. Useful when a transparent output is desired and supported by the output path.
fromSurface Choose the capture surface behavior. Leave at its default unless you have a specific reason to change it.
optimizeForSpeed Request speed-oriented screenshot encoding. Use when encoding speed matters more than the default behavior; check output suitability for your use.

Options can interact: clip describes a rectangle, while fullPage asks for the full page. Keep the capture scope unambiguous and review the resulting image when combining specialized options.

5. Capture one element instead

If the target is a single chart, card, or component, use an element handle’s screenshot() rather than capturing the entire page. Puppeteer scrolls the element into view if needed. The call throws if the element has been detached from the DOM.

const chart = await page.waitForSelector('#chart');
if (!chart) throw new Error('Chart was not found');
await chart.screenshot({ path: 'chart.png' });

Use page.screenshot({ fullPage: true }) for the full document; use elementHandle.screenshot() when the output should be limited to a particular element.

6. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its one-call endpoint returns an image or PDF, while handling browser setup for you. The ScreenshotNeo docs cover the request options.

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 request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

Before capture, ScreenshotNeo accepts cookie and 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 page verdict and billing headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up for free and capture 1,000 screenshots a month with no card.

7. Performance, reliability, and cost

  • Image size and memory: Full-page images can be tall and use more memory than viewport captures. Large pages may take longer to render and encode. Save directly with path when you do not need the bytes in application memory.
  • Browser lifecycle: Close the browser in a finally block so failures do not leave browser processes running. If you reuse a browser for multiple jobs, ensure each page is closed after use and account for concurrent work.
  • Concurrency: Puppeteer documents that, during a screenshot in a BrowserContext, calls such as newPage() and close() wait for the screenshot to finish, while bringToFront() does not. Avoid assuming every browser operation proceeds independently during capture.
  • Stability: Pick a page-specific readiness condition, handle navigation and selector timeouts, and bound retries. Retrying an unchanged failure indefinitely can waste time and resources.
  • Cost: Puppeteer is an open-source browser automation library, but running captures still consumes compute, memory, storage, and network resources on the machine or service where it runs. The dossier gives no universal runtime or cost benchmark; measure against your own page set and workload.

8. Troubleshooting

Symptom Likely cause Fix
Only the visible viewport appears. fullPage was omitted or set to false. Set fullPage: true in the screenshot options.
No file appears. No path was supplied, or the path points somewhere unexpected. Set an explicit output path and inspect the process working directory. Without path, consume the returned bytes instead.
Wrong image type or quality setting seems ignored. The path extension and requested type may not match, or quality was used with PNG. Choose a matching extension and output type. Use quality only with JPEG or WebP.
Some sections or images are missing. They had not rendered or lazy-loaded when capture began. Wait for the content-specific selector or state; for lazy loading, scroll through required content first and wait for it to appear.
Capture hangs or navigation times out. The page may keep connections open, render slowly, or wait on third-party resources. Choose an appropriate navigation wait condition, then wait for a specific readiness signal. Set reasonable timeouts and handle errors; network idle is not a universal page-ready test.
Element screenshot throws. The element was detached or did not resolve. Wait for the selector, re-query after page updates, and capture while the element remains attached.
Browser process remains after an error. Cleanup was skipped when an earlier step threw. Close the browser from a finally block.
Screenshot is unexpectedly huge. The page is exceptionally tall or includes content you do not need. Capture a specific element or a deliberate clip, or limit the page content loaded before capture.

9. Frequently asked questions

Does fullPage: true scroll the page?

It requests a full-page screenshot. It does not guarantee that page scripts have loaded every lazy or infinite-scroll item; wait for the content your use case requires.

Can I get the screenshot without writing a file?

Yes. Omit path and use the returned bytes, or request base64 encoding when a string representation is needed.

How do I capture a full page as JPEG?

Set the screenshot type to JPEG and provide a matching .jpg path. The quality option applies to JPEG and WebP, not PNG.

How do I capture only a page section?

Use an element handle’s screenshot() method for a DOM element, or use a clip rectangle when the desired region is defined by coordinates.