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

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

Call page.screenshot() with fullPage: true. Puppeteer then captures the document beyond the current viewport. The smallest complete example is to launch a browser, open the URL, wait for navigation, save the image, and close the browser.

Minimal Puppeteer full-page screenshot

Install Puppeteer in a Node.js project, then save this as screenshot.mjs:

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();
}

Run npm install puppeteer, followed by node screenshot.mjs. The resulting page.png contains the full rendered page rather than only the visible viewport. The official Screenshots guide uses this same launch, navigation, capture, and close sequence.

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

What fullPage changes

fullPage belongs to Puppeteer’s page-level screenshot method and defaults to false. Setting it to true asks for the entire page. It is different from a viewport screenshot, which is limited to the current browser window. See the ScreenshotOptions reference for the documented option definitions.

The capture represents the page state at the instant the screenshot runs. A navigation wait is necessary, but it is not proof that every application-rendered component, image, animation, or lazy-loaded section is ready. Treat networkidle2 as a useful starting point, then add a readiness signal for the site you are capturing.

Set up a repeatable capture script

1. Install and launch

Create a project and install Puppeteer:

mkdir page-capture
cd page-capture
npm init -y
npm install puppeteer

Use an .mjs file (or enable ES modules in package.json) so the import statement in the example runs directly. The finally block matters: it closes Chromium even when navigation or screenshot capture throws an error.

2. Choose a navigation wait

waitUntil: 'networkidle2' waits for a period with no more than two active network connections. It is the condition shown in Puppeteer’s guide, not a universal “page is finished” test. Single-page applications can render after that point, and some sites keep analytics or streaming connections open indefinitely.

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

When the page exposes a reliable application signal, wait for it explicitly:

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

Replace the selector with a marker your application adds only after the content needed in the image is present. A fixed delay can help with a known animation, but a selector or other application-specific condition is usually more deterministic.

3. Trigger lazy-loaded content when necessary

Full-page capture does not guarantee that every lazy image has already loaded. If the page loads sections only after scrolling, scroll it before capturing and wait for the page’s own loading signal. This example is a starting pattern, not a universal lazy-loading solution:

await page.goto('https://example.com/articles', { waitUntil: 'networkidle2' });
await page.evaluate(async () => {
  await new Promise(resolve => {
    let y = 0;
    const step = 600;
    const timer = setInterval(() => {
      window.scrollBy(0, step);
      y += step;
      if (window.innerHeight + window.scrollY >= document.body.scrollHeight || y > 100000) {
        clearInterval(timer);
        resolve();
      }
    }, 100);
  });
});
await page.screenshot({ path: 'articles.png', fullPage: true });

For production jobs, prefer a page-specific signal that images have completed, and consider waiting for a selector after the scroll. Pages with infinite scrolling may never reach a final height; impose a limit or capture a defined state instead.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Save an image or keep it in memory

The path option writes the result to disk. Puppeteer infers the image type from the filename extension when a path is supplied. If you omit path, the method returns image data instead of creating a file. By default that result is a Uint8Array; requesting base64 encoding returns a string, as documented for Page.screenshot().

const bytes = await page.screenshot({ fullPage: true });
await writeFile('page.png', bytes);

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

Use the byte form when uploading directly to object storage or returning an HTTP response. Use base64 only when the receiving interface requires text; it increases the amount of data that must be represented in memory.

Screenshot options that matter for whole pages

Option What it does Important detail
fullPage Captures the full document when true. Defaults to false.
path Saves the image to a file. The extension determines the image type when a path is supplied.
type Selects the image format. The documented default is PNG.
encoding Controls the returned data representation. The documented default is binary; base64 returns a string.
clip Restricts the capture to a rectangle. A clip changes the capture area, so it is not an unbounded whole-page image.
captureBeyondViewport Controls capture outside the viewport. The reference documents a default of false with no clip and true when a clip is present.
omitBackground Omits the default background. Useful when you need transparency and the page supports it.
quality Controls lossy image quality. Applies to formats other than PNG.
fromSurface Selects the browser surface used for capture. Keep protocol-specific behavior in mind when changing it.
optimizeForSpeed Requests speed-oriented encoding. Choose it only when its output trade-off suits your workflow.

Not every option is available in every browser or protocol mode. In particular, Puppeteer’s WebDriver BiDi documentation lists the supported screenshot parameters and warns that the complete screenshot option set is not supported there.

Whole page versus one element

Use Page.screenshot({ fullPage: true }) for the document. If the requirement is a card, chart, invoice, or other single node, locate that node and call ElementHandle.screenshot() instead:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const card = await page.waitForSelector('.invoice-card');
if (!card) throw new Error('Invoice card was not found');
await card.screenshot({ path: 'invoice-card.png' });

ElementHandle.screenshot() scrolls the element into view when needed. It can fail if the element has been detached from the DOM between lookup and capture, which is common in frequently re-rendered interfaces; locate it again after the render settles.

Control the rendering environment

A whole-page image is affected by the viewport, device scale factor, color scheme, fonts, and authentication state. Set these before navigation when they are part of the result you want:

await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.emulateMediaFeatures([
  { name: 'prefers-color-scheme', value: 'light' },
]);
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'light-desktop.png', fullPage: true });

Use a consistent viewport in automated comparisons. If a site serves different markup to mobile and desktop, capture each intended viewport separately rather than assuming one “full page” represents all layouts.

Troubleshoot incomplete or failed captures

The image stops at the viewport height

  • Confirm the option is exactly fullPage: true on page.screenshot(), not on an unrelated call.
  • Check whether a clip rectangle limits the capture.
  • Make sure you are opening the newly written file, not an older artifact from a previous run.

Images or lower sections are blank

  • networkidle2 may have occurred before application rendering finished.
  • Wait for a page-specific selector, scroll to activate lazy loading, and wait for the page’s image-ready signal.
  • Verify that the browser process can reach the image host and that required fonts or resources are not blocked.

Navigation never completes

  • Long-lived connections can prevent a network-idle condition.
  • Use domcontentloaded and then wait for the exact selector or state your application defines as ready.
  • Keep a job timeout around navigation so a broken page cannot hold a worker forever.

An element screenshot throws a detached-node error

The framework replaced the node after you selected it. Wait for the re-render to finish, select the element again, and capture the fresh handle.

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

BiDi rejects an option

Consult the supported-parameter list in the BiDi documentation. Switch to an option supported by that protocol or use the normal Puppeteer protocol mode when your deployment permits it.

Best Value
The SQL Programming Language: .
  • Used Book in Good Condition
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and concurrency

Full-page images can be substantially taller than the viewport, so memory and encoding time grow with document dimensions and image format. Keep pages bounded when infinite scrolling is involved, avoid needlessly large device scale factors, and choose JPEG or WebP only when their quality characteristics fit the use case. PNG remains the documented default.

Close every browser you launch. For a service that handles many URLs, reuse a browser process carefully while isolating pages or browser contexts, and enforce navigation and capture timeouts at the job level. Puppeteer coordinates some operations automatically: within a BrowserContext, newPage(), Browser.newPage(), and Page.close() wait for screenshot completion. Page.bringToFront() does not wait for an existing screenshot, so do not use it as a completion signal when coordinating concurrent work.

Or skip the browser setup

ScreenshotNeo provides a single-request website screenshot API when you do not want to operate Chromium yourself. Before capture it accepts the cookie or consent banner as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

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

Use the API key and target URL in one GET request (the ScreenshotNeo documentation lists all options):

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

The same request from Python:

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)

Or from Node.js without launching Puppeteer:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const data = Buffer.from(await res.arrayBuffer());

Every ScreenshotNeo feature is included on every plan. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots, followed by Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000. Yearly billing provides two months free. Create a free ScreenshotNeo account to get the 1,000 monthly screenshots without a card.

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.