ScreenshotNeo

BlogHow-to

How to Generate Full-Page Website Screenshots with Puppeteer for Reports

Capture a complete rendered web page with Puppeteer, choose image or PDF output, and handle readiness, responsive layouts, errors, and repeatability.

By the ScreenshotNeo team4 October 20269 min read

Use Puppeteer’s Page.screenshot() with fullPage: true to capture a website from top to bottom as one image. Set the viewport before navigating if the report must show a particular desktop or mobile layout, then wait for the page content your report depends on and save the capture to a file. The example below writes a PNG:

import puppeteer from 'puppeteer';

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

networkidle2 is a useful starting point, not proof that every lazy image, animation, or client-rendered section is ready. Add a wait tailored to the page when needed. This guide covers a repeatable image workflow, format and capture options, PDF alternatives, troubleshooting, and a hosted option for jobs where managing a browser is unnecessary. Puppeteer’s screenshot guide documents Page.screenshot().

1. Install Puppeteer and capture a full page

In a new Node.js project, install Puppeteer:

npm install puppeteer

Save this as capture.mjs and run it with node capture.mjs. Replace the example URL with the page you are authorized to capture.

import puppeteer from 'puppeteer';

const url = 'https://example.com';
const browser = await puppeteer.launch();

try {
  const page = await browser.newPage();

  // Set the viewport before navigation so the page lays out at this size.
  await page.setViewport({ width: 1440, height: 900 });

  await page.goto(url, {
    waitUntil: 'networkidle2',
    timeout: 60_000,
  });

  await page.screenshot({
    path: 'report.png',
    fullPage: true,
  });
} finally {
  await browser.close();
}

The viewport controls the rendered layout width and initial browser viewport height; fullPage asks Puppeteer to capture the full document. The screenshot options reference lists fullPage as false by default, so set it explicitly. The file extension can determine the image format when type is omitted. See the ScreenshotOptions reference.

Use a stable output path

A relative path resolves from the process working directory. For scheduled report jobs, use a known output directory and create it before capture; Puppeteer saves the file but the example does not create parent directories. Keep the URL, viewport, output format, and readiness condition with the report run settings so a later capture can reproduce the intended layout.

2. Wait for the page state your report needs

Navigation completion and visual completeness are different. Sites may load sections or images only when they approach the viewport, keep network connections open, or display temporary overlays. No single navigation wait condition guarantees that every site has finished presenting the content you want.

Choose a navigation wait condition

  • waitUntil: 'load' waits for the load event. Use it when the page’s load event is a suitable baseline.
  • waitUntil: 'domcontentloaded' waits for initial HTML parsing and can be useful when application readiness is checked separately.
  • waitUntil: 'networkidle2' is the Puppeteer guide’s example. Treat it as a starting point; analytics, polling, or other persistent requests can affect it.

For a page with a known report section, wait for that selector after navigation:

await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60_000 });
await page.waitForSelector('[data-report-ready="true"]', { timeout: 15_000 });
await page.screenshot({ path: 'report.png', fullPage: true });

Use a selector that actually indicates the relevant content is ready. A selector merely appearing does not establish that every image or chart inside it has finished rendering; add a page-specific check if the report depends on those elements.

Lazy-loaded images and long pages

A full-page capture request does not establish that every site’s deferred content has loaded. Inspect representative output from the target site. If images load as the page scrolls, a site-specific preparation step can scroll through the document before capturing. This may trigger lazy loading, but the site’s own behavior determines whether it works:

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

// If the page exposes a reliable ready marker, wait for it here.
await page.screenshot({ path: 'report.png', fullPage: true });

The delay is an example value, not a general readiness guarantee. For pages whose content expands as you scroll, a one-pass scroll may not reach the final height; use a page-specific completion condition and verify the image. Animations can also produce inconsistent frames, so disable them with site-specific CSS or wait until the relevant animation ends when a stable report image matters.

3. Select viewport, format, and capture area

Set the viewport before navigating when responsive behavior matters. Puppeteer notes that some sites may not expect viewport changes; changing dimensions after page load can trigger layout changes. The Page API documents viewport behavior.

// Desktop report layout
await page.setViewport({ width: 1440, height: 900 });

// Mobile report layout
await page.setViewport({ width: 390, height: 844, isMobile: true });

Choose dimensions that match the report’s purpose. A narrower viewport may change navigation, column count, and content order, not merely scale the desktop result down.

Need Option Notes
Entire document fullPage: true Captures the full page instead of only the current viewport.
Specific region clip Supply a clip rectangle when only a defined page area belongs in the report.
PNG, JPEG, or WebP type or file extension The extension can infer the format when type is omitted. Check the current API reference for supported formats.
Lossy image compression quality Applies to formats that support quality settings; it does not apply to PNG.
Transparent background omitBackground Useful for compositing where supported by the page and output format.
In-memory bytes Omit path Page.screenshot() can return image data for a pipeline that stores or transforms it elsewhere.

Option names and defaults can be version-sensitive. Check the official screenshot options reference when pinning behavior in a production job. A huge full-page image can be awkward to embed in a report; consider whether a clipped image or PDF is a better deliverable.

4. Image screenshot or PDF report?

Use fullPage when reviewers need one continuous image of the rendered page. Use page.pdf() when the deliverable should have paper dimensions, margins, or page ranges. Puppeteer PDF generation uses print CSS media by default. To use screen styling, emulate screen media before generating the PDF.

await page.emulateMediaType('screen');
await page.pdf({
  path: 'report.pdf',
  format: 'A4',
  printBackground: true,
  margin: {
    top: '12mm',
    right: '12mm',
    bottom: '12mm',
    left: '12mm',
  },
});

PDF options include paper format, margins, scale, background printing, landscape orientation, and page ranges. PDF output modifies colors for printing by default; the Puppeteer documentation points to -webkit-print-color-adjust for exact colors. See the PDFOptions reference and Page API. Navigating to an existing PDF URL is different from generating a PDF with page.pdf(); Puppeteer’s API notes that headless shell mode does not support navigation to PDF documents.

5. Capture a single element instead of the full page

If the report needs a chart, card, or other component, capture that element rather than a very tall document:

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

The element screenshot helper scrolls the target into view when needed. This can make an element capture a better fit for a focused report, though it will not include surrounding page context. Puppeteer’s screenshot guide describes element screenshots.

6. Make report captures reliable

  1. Fix the viewport. Choose and record the dimensions before navigation.
  2. Set a navigation timeout. Handle slow or unresponsive pages instead of letting jobs wait indefinitely.
  3. Wait for meaningful readiness. Prefer a stable, page-specific selector or application signal when available.
  4. Keep browser cleanup in finally. Close the browser after success or failure.
  5. Validate output. Check that the file exists, has nonzero bytes, and looks complete before attaching it to a report.
  6. Keep failures visible. Record the target URL and error context so a failed capture can be investigated or retried deliberately.

Screenshot capture coordinates with page lifecycle operations: the Page API documents that, in a browser context, calls such as creating or closing pages wait for screenshot work to finish. Avoid treating screenshot capture as an instantaneous operation when coordinating concurrent page work. See Page.screenshot().

7. Troubleshooting

Symptom Likely cause Fix
Lower sections or images are missing Deferred content was not ready when capture began. Wait for a page-specific ready selector; inspect lazy-loading behavior and use a site-specific scroll or readiness step.
Navigation times out The page is slow, keeps requests open, or never reaches the selected lifecycle condition. Set an explicit timeout and try a different navigation condition, followed by an application-specific readiness check.
Screenshot shows a consent banner, chat panel, or popup The page rendered an overlay as part of its current state. For your own site, configure the page state or test environment. For third-party sites, respect access controls and site terms; a screenshot faithfully reflects what rendered.
Mobile layout is unexpectedly desktop-like The viewport was set too late, or its width does not trigger the site’s mobile breakpoint. Set viewport dimensions before navigation and choose a width matching the desired responsive state.
Colors or page breaks differ in PDF PDF uses print media by default and applies print color adjustments. Use emulateMediaType('screen') for screen styling, review print CSS, and set PDF options deliberately.
Output file is absent The relative path resolved from an unexpected working directory or the parent directory does not exist. Use an explicit output location and ensure its directory exists before capture.
Browser process remains after an error Cleanup was not guaranteed through every code path. Put browser.close() in a finally block.
Very tall image is unwieldy A full document image can have large dimensions and file size. Capture a specific element or clipped region, or choose a paginated PDF for report distribution.

8. Performance, reliability, and cost

A Puppeteer workflow runs a browser for each capture job or batch and must account for navigation, rendering, image output, and cleanup. There is no universal timing or memory figure for this workflow in the cited documentation, so measure it against the pages and concurrency your reporting process uses. Very tall pages and image-heavy documents can increase output size and resource use; capture only the content the report needs when a full document is not necessary.

For reliability, pin the Puppeteer version used by the job, keep readiness checks specific to the target application, and inspect representative images after site changes. The options reference can change between releases; verify version-sensitive defaults when upgrading. A browser-based process has no per-screenshot service price in this approach, but it does carry the operational cost of running and maintaining the browser environment.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request can return a screenshot or PDF, and its API documentation covers the available options. For example, save a full-page WebP capture of a report target:

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("report.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.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(fs => fs.writeFile('report.webp', Buffer.from(await res.arrayBuffer())));

Cookie banners are accepted before capture, and 60+ known consent platforms, newsletter popups, and chat widgets are removed; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 shots a month without a card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does fullPage: true capture content below the fold?

It requests a full-document screenshot. Whether deferred or interactive content has rendered is a separate, site-specific readiness question.

Can I return screenshot data without writing a file?

Yes. Omit path and handle the returned image data in your application.

Should I use a screenshot or PDF in a report?

Use an image for one continuous visual record of the rendered page; use PDF when page sizing, margins, or print-oriented distribution matters.

Can I use the same capture for desktop and mobile reports?

Capture each desired responsive layout with its own viewport set before navigation. The site may rearrange or replace content at different widths.

Official references