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

To stop html2canvas from clipping a long or wide element, render it at its complete scrollable size instead of the current viewport: set windowWidth to element.scrollWidth and windowHeight to element.scrollHeight. Then verify the resulting pixel dimensions, control scale, set the intended scroll offsets, and handle cross-origin images separately. Browser canvas limits can still require tiled captures.

The reliable full-element capture

Start by measuring the element immediately before capture. scrollWidth and scrollHeight include content that is outside the visible box, including overflow created by long text, lazy sections that have been laid out, and horizontally scrolling content.

const element = document.querySelector('#capture');

if (!element) {
  throw new Error('Could not find #capture');
}

const canvas = await html2canvas(element, {
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight,
  scrollX: 0,
  scrollY: 0,
  scale: 1,
  backgroundColor: '#fff'
});

document.body.appendChild(canvas);

The windowWidth and windowHeight settings tell html2canvas what rendering window to emulate. They do not directly set the output bitmap’s dimensions; CSS geometry and scale determine the final internal pixel size. If you need a file, export the canvas after checking it:

const link = document.createElement('a');
link.download = 'capture.png';
link.href = canvas.toDataURL('image/png');
link.click();

Use the same measurement for the exact element you pass to html2canvas. Measuring a parent while capturing a child, or measuring before content expands, can leave the bottom or right edge outside the requested render area.

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

Why clipping happens

The viewport is smaller than the element

Without explicit dimensions, rendering commonly follows the document’s current view. A 1,200-pixel-tall element viewed through an 800-pixel viewport can therefore produce an image that ends early. Matching the rendering window to the element’s scrollable dimensions fixes this geometry mismatch.

Explicit crop options override your intent

The options width and height define the canvas dimensions, while x and y define the crop origin. Supplying a smaller width or height intentionally captures only a region. Remove those options for a full-element shot, or calculate them from a region you deliberately want.

const region = document.querySelector('#capture .invoice');
const canvas = await html2canvas(region, {
  x: 0,
  y: 0,
  width: region.scrollWidth,
  height: region.scrollHeight,
  windowWidth: region.scrollWidth,
  windowHeight: region.scrollHeight,
  scale: 1
});

High-DPI scaling multiplies memory and limits

scale defaults to window.devicePixelRatio. On a Retina display, a 10,000 by 10,000 CSS-pixel request can become roughly 20,000 by 20,000 internal pixels. That increases memory use and can push the canvas past a browser’s maximum dimension or area. Set scale: 1 when predictable output dimensions matter or when a capture is failing silently. Increase it only after confirming that the resulting bitmap remains within safe limits.

The page is scrolled or contains fixed-position UI

The source defaults scrollX and scrollY to the document view’s current page offsets. A capture taken halfway down a page can consequently place fixed headers, sticky controls, or viewport-relative content differently than expected. Set both values explicitly for reproducibility:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const canvas = await html2canvas(element, {
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight,
  scrollX: 0,
  scrollY: 0,
  scale: 1
});

If your design intentionally depends on a scrolled position, pass the measured offsets instead of zero and record them with the capture settings.

Browser canvas limits: when correct settings still fail

The html2canvas FAQ warns that “The canvas may hit browser size limits.” Approximate guidance accessed in 2026 is:

Browser Approximate maximum dimension Approximate maximum area
Chrome/Chromium 32,767 pixels 268 million pixels
Firefox 32,767 pixels 472 million pixels
Desktop Safari 32,767 pixels Similar area behavior to Chrome
iOS Safari Lower and dependent on device RAM Device-dependent

These are rough, browser-dependent figures, not guarantees. When a canvas exceeds a limit, the browser can silently return a blank or partially rendered output without throwing an exception. Calculate the internal size before capture:

const width = element.scrollWidth;
const height = element.scrollHeight;
const scale = 1;
const pixels = width * scale * height * scale;

console.log({
  cssWidth: width,
  cssHeight: height,
  pixelWidth: width * scale,
  pixelHeight: height * scale,
  pixels
});

For oversized pages, lower scale, reduce the requested width or height, or capture smaller sections. Tiled captures avoid asking the browser for one enormous bitmap.

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

Capture in tiles

Give each tile its own crop origin and dimensions, then stitch the resulting canvases into a destination canvas or process them as separate pages. A basic vertical tiling pattern is:

async function captureInTiles(element, tileHeight = 4000) {
  const width = element.scrollWidth;
  const height = element.scrollHeight;
  const tiles = [];

  for (let y = 0; y < height; y += tileHeight) {
    const h = Math.min(tileHeight, height - y);
    const tile = await html2canvas(element, {
      x: 0,
      y,
      width,
      height: h,
      windowWidth: width,
      windowHeight: height,
      scrollX: 0,
      scrollY: 0,
      scale: 1
    });
    tiles.push({ y, canvas: tile });
  }

  return { width, height, tiles };
}

Stitch tiles only when the content is stable between calls. Freeze animations, avoid changing timestamps, and wait for images and fonts before starting; otherwise seams can show different layout states.

Images that are missing are not necessarily clipped

A blank image area often indicates a cross-origin loading problem rather than a crop. By default, allowTaint: false prevents unsafe images from being drawn. useCORS: true can work when the image server sends the required CORS response header:

const canvas = await html2canvas(element, {
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight,
  useCORS: true,
  scale: 1
});

Do not expect useCORS to bypass server policy. If the remote server does not authorize your origin, configure a proxy you control and pass its URL:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const canvas = await html2canvas(element, {
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight,
  useCORS: true,
  proxy: 'https://your-domain.example/html2canvas-proxy'
});

Only proxy resources you are permitted to retrieve. Cross-origin iframes are a separate browser-security boundary: html2canvas cannot render an iframe whose contentDocument is inaccessible. Capture content from the iframe’s own origin or use an application-level export instead.

A production-ready configuration

This version combines the usual geometry, scroll, background, and image settings while retaining predictable output dimensions:

async function screenshot(selector) {
  const element = document.querySelector(selector);
  if (!element) throw new Error(`No element matches ${selector}`);

  await document.fonts?.ready;
  const width = element.scrollWidth;
  const height = element.scrollHeight;

  console.log({ width, height, devicePixelRatio: window.devicePixelRatio });

  return html2canvas(element, {
    windowWidth: width,
    windowHeight: height,
    scrollX: 0,
    scrollY: 0,
    scale: 1,
    useCORS: true,
    backgroundColor: '#fff'
  });
}

const canvas = await screenshot('#capture');

Use useCORS only for resources whose servers are configured to allow the requesting origin. If your design needs transparency, replace backgroundColor: '#fff' with backgroundColor: null.

Debugging checklist

  1. Measure first: log scrollWidth and scrollHeight immediately before capture.
  2. Match the render window: set windowWidth and windowHeight to those values.
  3. Inspect crops: remove or verify explicit x, y, width, and height.
  4. Check internal pixels: multiply each CSS dimension by scale; try scale: 1 if the result is huge or blank.
  5. Control scrolling: set scrollX and scrollY, especially when fixed-position elements are involved.
  6. Classify missing images: inspect CORS headers and iframe origin before treating the symptom as clipping.
  7. Split oversized work: tile or paginate when browser limits are approached.

Common symptoms and fixes

Symptom Likely cause Fix
Bottom of a long page is absent Rendering window follows the viewport Set both window dimensions from scrollWidth and scrollHeight.
Right edge is cut off Horizontal overflow was not included Use the element’s scrollWidth; remove a restrictive width.
Blank or half-rendered canvas Browser dimension or area limit Lower scale or tile the capture.
Images absent while text is complete Cross-origin policy Use permitted CORS headers or a controlled proxy.
Fixed header appears in the wrong place Unexpected page offsets Set scrollX and scrollY deliberately.
Iframe content is empty Cross-origin iframe isolation Capture within the iframe’s origin or provide an export endpoint.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you need a clean screenshot from a URL rather than a canvas assembled in your page, ScreenshotNeo provides a single GET request and returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.

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

Here is the cURL call (see the ScreenshotNeo documentation for 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

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)

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

ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, custom JavaScript and CSS, waits for selectors, delays or network idle, request blocking, cookies and headers, device presets, arbitrary viewports, retina scale, PDF page controls, caching TTLs, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, and an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. Every feature is on every plan. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Frequently Asked Questions

Should I always use scale: 1?

No. It is the predictable, lower-memory choice. Raise it only when the resulting pixel dimensions remain safely below your target browser’s limits and you need more output detail.

Can html2canvas capture a page from another domain?

It can draw permitted external images when CORS headers or a controlled proxy allow them, but it cannot read a cross-origin iframe’s inaccessible document.

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

Why does changing browser zoom alter the result?

Zoom and device pixel ratio can change the effective scale and internal bitmap size. Log the dimensions and set an explicit scale when reproducibility matters.

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.