October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk9 min

Why html2canvas ForeignObjectRendering Captures Only the Viewport (and How to Capture the Full Page)

ForeignObjectRendering does not automatically mean full-page capture. html2canvas defaults to viewport dimensions; use measured scrollWidth and scrollHeight for the cloning window and output, then account for scroll position, browser canvas limits and renderer differences.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The usual cause is a viewport-sized render boundary. html2canvas defaults windowWidth and windowHeight to window.innerWidth and window.innerHeight. ForeignObjectRendering then serializes and draws an SVG foreignObject using the configured render width and height. If those values remain equal to the visible viewport, content below or beside it is outside the canvas even when the document itself is much larger.

Measure the target element, pass its scroll dimensions as the cloning window, and set explicit output dimensions:

The working full-page configuration

const element = document.documentElement;

const canvas = await html2canvas(element, {
  foreignObjectRendering: true,
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight,
  width: element.scrollWidth,
  height: element.scrollHeight
});

document.body.appendChild(canvas);

The project FAQ recommends windowWidth: element.scrollWidth and windowHeight: element.scrollHeight when output is cut off. Supplying width and height makes the intended canvas boundary explicit. This example targets the root document; use the actual element you intend to capture if you need a component or a nested scrolling region.

Why the default call follows the visible viewport

windowWidth and windowHeight are cloning bounds

At the entry point, html2canvas obtains default window dimensions from the document’s default view. In a normal browser window, those defaults are the current innerWidth and innerHeight. It creates window bounds with those values and clones the document using them. Turning on foreignObjectRendering changes the renderer, not those defaults.

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

That is why this call commonly captures only what you can see:

const canvas = await html2canvas(document.documentElement, {
  foreignObjectRendering: true
});

The page may have a large scrollHeight, but the cloned render window is still viewport-sized. Anything outside that boundary can be omitted from the serialized output.

ForeignObjectRenderer uses the render dimensions

In the current source, ForeignObjectRenderer creates a canvas with options.width * options.scale by options.height * options.scale. It creates an SVG foreignObject with those same scaled dimensions, loads the serialized SVG as an image, and draws it into the canvas after translating by the configured x and y offsets. A small width or height therefore produces a small drawing surface, regardless of how far the document can scroll.

The source labels this renderer experimental. Browser behavior, CSS support and external resources can therefore differ from the default renderer.

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

Measure before changing options

Do not assume that the document’s scroll size is the same as the element you want. Inspect all three measurements:

const element = document.documentElement;
const rect = element.getBoundingClientRect();

console.table({
  rectWidth: rect.width,
  rectHeight: rect.height,
  scrollWidth: element.scrollWidth,
  scrollHeight: element.scrollHeight,
  viewportWidth: window.innerWidth,
  viewportHeight: window.innerHeight
});
  • getBoundingClientRect() describes the element’s current layout box in the viewport.
  • scrollWidth includes horizontally overflowing content and the element’s scrollable width.
  • scrollHeight includes vertically overflowing content and the element’s scrollable height.
  • innerWidth and innerHeight describe the browser viewport that html2canvas uses by default.

If a page has a horizontal overflow region, use its measured scrollWidth as well as its height. If you are capturing a nested element, read dimensions from that element rather than automatically using document.documentElement.

A robust capture helper

This helper validates dimensions, lets you choose the target, and makes the scroll position explicit:

async function captureFullPage(target = document.documentElement) {
  const width = target.scrollWidth;
  const height = target.scrollHeight;

  if (!width || !height) {
    throw new Error(`Target has invalid dimensions: ${width} x ${height}`);
  }

  return html2canvas(target, {
    foreignObjectRendering: true,
    windowWidth: width,
    windowHeight: height,
    width,
    height,
    scrollX: window.scrollX,
    scrollY: window.scrollY
  });
}

const canvas = await captureFullPage();
const link = document.createElement('a');
link.download = 'full-page.png';
link.href = canvas.toDataURL('image/png');
link.click();

scrollX and scrollY are the scroll positions used for rendering. Set them deliberately when fixed-position content, a scrolled target or a component inside a scrolling container affects the result. For a reproducible page capture, record the values you use and avoid changing the page between measuring and rendering.

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

What each option controls

Option Purpose Typical full-page choice
foreignObjectRendering Selects the experimental SVG foreignObject-based renderer. true
windowWidth Width of the window bounds used while cloning the document. Target scrollWidth
windowHeight Height of the window bounds used while cloning the document. Target scrollHeight
width Output render width before scaling. Target scrollWidth
height Output render height before scaling. Target scrollHeight
scale Multiplies the renderer’s canvas and SVG dimensions. Choose with browser limits in mind
scrollX, scrollY Scroll positions used during rendering. Set explicitly for scrolled or fixed-position cases

Do not confuse the cloning window with the output size. Passing only width and height can leave the cloned document constrained by viewport defaults; passing only windowWidth and windowHeight can leave the output boundary implicit. For a predictable full-page attempt, set both pairs from measured dimensions.

Fixed elements, nested scrollers and dynamic content

Fixed and sticky UI

A fixed header or chat panel is positioned relative to a viewport or containing block, not simply appended below normal flow. Decide whether it should appear once at its rendered position or be hidden for a clean document image. Set scrollX and scrollY deliberately, and test at the scroll position your output is meant to represent.

Nested scrolling containers

If the page contains a panel with its own scrollbar, the root document’s scrollHeight may not represent the panel’s complete content. Capture that panel and use its own scrollWidth and scrollHeight, or temporarily arrange the panel so its content is expanded before measuring. The important rule is to measure the element whose pixels you want.

Lazy-loaded and late layout changes

Images, fonts and scripts can change layout after the first measurement. Wait until the content is in its final state, then measure and call html2canvas. If the page changes while the clone is being built, the measured boundary and the actual layout can disagree; freeze animations and avoid mutating the DOM during capture when possible.

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

Canvas limits can look like a renderer bug

The html2canvas FAQ warns that the canvas may hit browser size limits. Maximum width, height and total pixel area vary by browser and platform. The FAQ gives rough evergreen-browser maximum dimensions around 32,767 pixels, while area limits also vary. An oversized canvas may be blank or partially rendered without throwing an exception.

Reduce the risk

  • Log the measured width, height and scale before rendering.
  • Capture a smaller element or split a very long document into sections.
  • Lower scale when the resulting pixel dimensions are excessive.
  • Try the default renderer as a diagnostic comparison.
  • Test in the browser and platform that will run the production capture.

The effective pixel dimensions are approximately width × scale by height × scale. A modest CSS size can become a very large bitmap at a high scale.

ForeignObjectRendering versus other capture approaches

Approach Dimension control How pixels are produced Important trade-offs
html2canvas default renderer Uses html2canvas options, including explicit dimensions. Reconstructs the page from DOM and CSS. CSS, image/font loading and cross-origin restrictions can affect fidelity.
html2canvas ForeignObjectRendering Uses the cloning bounds and the renderer’s width and height. Serializes the DOM into an SVG foreignObject, then draws it to a canvas. Experimental; browser support and CSS/resource behavior can vary.
Browser-native automation Typically controls a browser viewport and screenshot surface directly. Captures a native browser-rendered surface. Requires browser setup and has its own viewport, resource and deployment considerations.

html2canvas is not a native screenshot facility: it reconstructs a representation from the DOM and CSS in the page. Unsupported CSS, cross-origin resources and browser differences can change the result. Use the default renderer as a comparison when ForeignObjectRendering produces an unexpected image, but do not interpret one browser’s result as a universal guarantee.

Troubleshooting checklist

Only the visible viewport is captured

Cause: windowWidth, windowHeight, width or height remained at viewport-sized defaults. Fix: measure the target and pass scrollWidth/scrollHeight to all relevant options, as in the full-page example.

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

windowWidth and windowHeight appear ignored

Check that you are passing numbers from the intended element, not stale values captured before layout completed. Confirm that the target’s measured dimensions are larger than innerWidth/innerHeight, and inspect the resulting canvas dimensions. Historical issue #1754, opened on 2019-02-08 for html2canvas 1.0.0-alpha.12 in Firefox 56 on Windows 10, reported a ForeignObjectRendering case where explicit window dimensions behaved differently from the plain renderer. That is version-specific historical evidence, not proof that every current release has the same defect.

The bottom is blank or cut off with no exception

Check the browser’s maximum canvas dimensions and total area first. Reduce the target or scale, then retry. A blank or partial output can be a platform limit rather than a JavaScript error.

Content is shifted or fixed controls repeat

Set scrollX and scrollY deliberately, and test with fixed or sticky elements temporarily hidden. Verify that you are capturing the intended scrolling element rather than the root document.

Styles or images differ

Reduce the page to a reproducible example and compare the default renderer. ForeignObjectRendering depends on browser support for the serialized SVG and on resource availability. Cross-origin images, fonts and unsupported CSS can prevent a pixel-for-pixel result even when the dimensions are correct.

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

The canvas is unexpectedly huge

Print scrollWidth, scrollHeight and scale. An off-screen element, an accidental horizontal overflow or a very large child can inflate the boundary. Capture the specific content element instead of the whole document when that is what the user needs.

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 the goal is a dependable website image rather than a client-side DOM reconstruction, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP or PDF. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, 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.

Here is the one-call cURL version (the ScreenshotNeo documentation lists the 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 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 in 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(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Options for production captures

ScreenshotNeo supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets and arbitrary viewports, retina scale, PDF paper sizes/margins/orientation/page ranges, HTML/CSS-to-image, custom JavaScript and CSS, pre-capture clicks, hidden selectors, waits for a selector, delay or network idle, ad/tracker/request/resource blocking, custom headers/cookies/user agents and Authorization, timezone and geolocation, transparent backgrounds, image resizing, configurable-TTL caching, signed links for public <img> tags, asynchronous jobs with signed webhooks, bulk capture of 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.

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

Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients, so an AI agent can request captures without you building a browser harness. Plans include 1,000 shots per month free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to start.

Cost, reliability and operational choices

Client-side html2canvas

  • No screenshot-service request is required, but the page’s browser, CSS, resources and canvas limits determine the result.
  • Every user’s viewport, device pixel ratio, fonts and browser can produce different output.
  • Very tall pages may require splitting or reduced scale.

ScreenshotNeo

  • Only clean shots are billed; failed loads, bot checks, blank pages, timeouts and cache hits are identified and not billed.
  • Cleanup of consent banners, popups and chat widgets happens before capture.
  • Free usage is 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Yearly billing provides two months free, and every feature is available on every plan.

Choose html2canvas when the capture must run inside the current page and you need a client-side canvas. Choose an API when you need repeatable server-side captures, PDF output, automation controls or AI-agent integration without maintaining browser setup.

Frequently Asked Questions

Does setting only foreignObjectRendering: true make a screenshot full page?

No. That flag selects the renderer; it does not replace the default viewport-sized window bounds. Set the cloning and output dimensions from the target’s scroll size.

Should I use document.body or document.documentElement?

Use the element whose complete content you intend to capture and measure that element. For a document-wide attempt, document.documentElement is a clear starting point; nested scrolling regions require their own measurements.

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

Why can a correct configuration still produce a blank canvas?

The requested bitmap may exceed browser width, height or total-area limits. Reduce the target or scale, or capture in sections.

Is ForeignObjectRendering a native browser screenshot?

No. html2canvas reconstructs a representation from DOM and CSS, serializes it for the foreign-object renderer and draws it into a canvas.

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.

More from the Wire

  1. Shenzhen desk3 min
    HONOR Expands Beyond Smartphones With Humanoid Robot RevealHONOR said it unveiled its first humanoid robot at MWC 2026 and named shopping assistance, workplace inspections, and supportive companionship as intended uses. Later Robotics D1 claims and a reported…
  2. Cupertino desk5 min
    Apple Unveils AirPods Max 2: The Upgrade That Should Have Happened Years AgoAirPods Max 2 adds H2-powered audio features and Apple claims up to 1.5× more effective ANC, but its design, Smart Case, and 20-hour battery rating are unchanged. Wired lossless audio…
  3. Cupertino desk4 min
    Apple’s OLED Touch MacBooks Are Coming—but the Dynamic Island Is the Real GambleApple has not announced an OLED touchscreen MacBook, but reports point to high-end models arriving in late 2026 or early 2027. The reported Mac Dynamic Island could be useful, but…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

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.