October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
browser automation

How to Capture Elements Larger Than the Viewport Without Blank Space in Puppeteer

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

Use ElementHandle.screenshot() for a DOM element that is taller or wider than the current viewport. Puppeteer scrolls the element into view and captures it through Page.screenshot(). If you define a manual clip instead, obtain a non-null boundingBox() and set captureBeyondViewport: true explicitly. Do not use fullPage as a substitute: it captures the whole page, not an arbitrarily clipped element.

Choose the capture path that matches your target

Puppeteer 25.12.0 documents two useful approaches. The first targets a live DOM node; the second targets a coordinate rectangle.

Approach Best for Important behavior
ElementHandle.screenshot() One element selected from the DOM Scrolls the element into view, then uses Page.screenshot() to capture it. The handle must remain attached.
Page.screenshot({ clip, captureBeyondViewport }) An explicit rectangle, including a box calculated from an element Requires a valid clip. Set captureBeyondViewport: true when the rectangle extends outside the viewport.

In the current ScreenshotOptions documentation, captureBeyondViewport defaults to false when no clip is supplied and true when a clip is supplied. Setting it explicitly makes your intent clear and avoids relying on a default that may be misunderstood.

Primary method: screenshot the element handle

This is the shortest and usually the most reliable solution for an oversized element.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({headless: true});
try {
  const page = await browser.newPage();
  await page.setViewport({width: 1280, height: 800, deviceScaleFactor: 1});
  await page.goto('https://example.com/long-page', {waitUntil: 'networkidle2'});

  const element = await page.waitForSelector('.target', {visible: true});
  if (!element) throw new Error('Target element was not found');

  await element.screenshot({path: 'element.png'});
} finally {
  await browser.close();
}

The official API description says: “This method scrolls element into view if needed, and then uses Page.screenshot() to take a screenshot of the element.” See ElementHandle.screenshot() and the screenshots guide. A selector can match a panel, article, chart, table, or any other rendered node; the resulting image is the element’s box rather than the entire document.

Wait for the content that determines the size

An element can exist before its images, fonts, or client-rendered rows have finished changing its dimensions. Wait for a meaningful selector, a known application state, or image completion before taking the shot.

await page.waitForSelector('.target img');
await page.evaluate(async () => {
  if (document.fonts) await document.fonts.ready;
  await Promise.all([...document.images].map(img =>
    img.complete ? undefined : new Promise(resolve => {
      img.addEventListener('load', resolve, {once: true});
      img.addEventListener('error', resolve, {once: true});
    })
  ));
});
const element = await page.waitForSelector('.target', {visible: true});
if (!element) throw new Error('Target element was not found');
await element.screenshot({path: 'element.png'});

This wait is application-specific. It prevents a capture taken while a lazy-loaded region is still empty, but it cannot repair a selector that is detached or hidden by the page itself.

Explicit clip: use a bounding box and capture beyond the viewport

Use this path when you need to adjust coordinates, add padding, combine regions, or diagnose a blank result. boundingBox() returns coordinates relative to the main frame, with width and height in pixels. It returns null when the node is not in layout.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({headless: true});
try {
  const page = await browser.newPage();
  await page.setViewport({width: 1280, height: 800, deviceScaleFactor: 1});
  await page.goto('https://example.com/long-page', {waitUntil: 'networkidle2'});

  const element = await page.waitForSelector('.target', {visible: true});
  if (!element) throw new Error('Target element was not found');

  const clip = await element.boundingBox();
  if (!clip) throw new Error('Target element has no layout box');
  if (clip.width <= 0 || clip.height <= 0) {
    throw new Error(`Invalid box: ${clip.width}x${clip.height}`);
  }

  await page.screenshot({
    path: 'element-clipped.png',
    clip,
    captureBeyondViewport: true,
  });
} finally {
  await browser.close();
}

Read the box only after the page has laid out the target. If a CSS transition is changing its size, wait for the transition to finish or temporarily disable animations in a test-only stylesheet.

Adding safe padding

You can expand the rectangle, but keep coordinates and dimensions valid for the page and image limits.

const box = await element.boundingBox();
if (!box) throw new Error('Target element has no layout box');
const padding = 16;
const clip = {
  x: Math.max(0, box.x - padding),
  y: Math.max(0, box.y - padding),
  width: box.width + padding * 2,
  height: box.height + padding * 2,
};
await page.screenshot({path: 'padded.png', clip, captureBeyondViewport: true});

Why blank space or clipping appears

The handle is detached

Frameworks often replace a node during navigation or re-rendering. A handle obtained before that replacement is no longer attached. Re-select the element immediately before capture and avoid triggering a state change between selection and screenshot.

The element has no layout box

boundingBox() returns null for nodes that are display:none, detached, or otherwise absent from layout. Check visibility, the active tab or accordion state, and whether the selector points to a template rather than the rendered node.

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

A clip was taken without beyond-viewport capture

A rectangle extending below or beside the viewport can produce incomplete output when beyond-viewport capture is not enabled. Supply captureBeyondViewport: true with the clip and verify the installed Puppeteer and Chromium versions.

Lazy content is still empty

Scrolling the element into view does not guarantee that every nested image or virtualized row has rendered. Wait for content, trigger the application’s load mechanism, or capture after the page reports completion. For virtual lists, only mounted rows can be captured; you may need to change the application’s rendering strategy for a complete image.

Sticky, fixed, transformed, or nested-scrolling layouts

position: fixed, sticky headers, CSS transforms, and an inner element with overflow: auto can make the visual result differ from the element’s ordinary box. Inspect computed styles and scroll the correct container. A transformed element may have a box whose coordinates do not match the visual bounds you expect; test a handle screenshot and a manual clip separately.

Zero dimensions caused by timing or CSS

Log the box before capture:

console.log(await element.boundingBox());

If width or height is zero, wait for layout, remove a collapsed state, or correct the selector. Do not “fix” a zero box by inventing clip dimensions.

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

Diagnose the two APIs systematically

  1. Confirm the URL loaded and the expected frame is active.
  2. Resolve the selector with waitForSelector; fail loudly if it returns null.
  3. Check attachment and layout with boundingBox().
  4. Record the box, viewport, device scale factor, and installed Puppeteer and Chromium versions.
  5. Try element.screenshot() first. If you need coordinates, use Page.screenshot with the recorded box and explicit captureBeyondViewport: true.
  6. Inspect the page for lazy loading, nested scroll containers, transforms, fixed overlays, and animation.
  7. Open the resulting file and verify its pixel dimensions; a valid file with unexpected dimensions usually indicates layout or clip calculations rather than a PNG/JPEG encoding problem.

Viewport resizing: historical workaround, not a universal fix

An older Puppeteer issue, #1779, described oversized element clipping in version 0.13.0 and discussed enlarging the viewport. That discussion is historical. The Puppeteer changelog records an element-screenshot viewport-setting change in 21.9.0 and removal of viewport resizing from ElementHandle.screenshot() in 23.9.0 on November 21, 2024. Resizing can also trigger media-query and resize-event side effects. Use it only when you deliberately want the page to reflow at a different viewport, not as a blanket remedy for blank output.

Performance and reliability considerations

Keep the page state deterministic

Set the viewport and device scale factor before navigation, wait for the exact content needed, and disable nonessential animations in your test environment. A stable page makes repeated captures easier to compare.

Choose the smallest useful target

Element screenshots avoid rasterizing unrelated page content. A manual clip is useful for padding or coordinate-based workflows, but calculating it after layout is essential. Extremely large raster images consume memory; split a very long report into intentional sections when your downstream system cannot handle one huge bitmap.

Do not infer guarantees from one successful run

The official API pages define behavior and defaults, but do not publish a failure rate or a percentage improvement for any setting. Validate your own pages across their loading states and the Puppeteer/Chromium versions you deploy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a GET-based screenshot API when you do not want to maintain Puppeteer and Chromium. It accepts a URL and returns PNG, JPEG, WebP, or PDF. Before capture it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.

For a page-level capture:

curl -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 all options. The same request in 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)

And 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 fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. Every feature is included on every plan: the Free plan provides 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. You can sign up free.

Frequently asked questions

Does fullPage: true capture one oversized element?

No. fullPage describes a full-page capture. Use an element handle or a clip for one DOM region.

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

What if the target is inside an iframe?

Obtain the frame with Puppeteer’s frame APIs, select the element within that frame, and take the screenshot from the corresponding handle. A selector evaluated in the main page cannot reach an iframe’s document.

Can I capture an element that is intentionally hidden?

Not as rendered content. Make it visible and laid out first, or render a separate export view. A hidden node has no meaningful visual box.

Which Puppeteer version should I use?

Match your code to the version installed by the project. The behavior described here reflects the current API material identifying Puppeteer 25.12.0; Chromium changes can still affect rendering.

Frequently Asked Questions

Can a screenshot include content from a virtualized list that is not mounted?

No. Puppeteer captures rendered pixels. Ask the application to mount the required rows or create an export-specific view before capturing.

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.

Why does my clip include a fixed header or overlay?

Fixed and sticky elements are painted independently of normal flow. Hide them with page CSS for the capture or target a region whose visual composition intentionally includes them.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.