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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

To capture a complete, sharp html2canvas image, size the virtual window to the element’s scroll dimensions, choose a deliberate crop, render at an appropriate pixel scale, and wait for fonts and images before calling the library. Cross-origin assets still require server permission or a same-origin proxy; no html2canvas option can bypass browser security. The workflow below covers full-page and regional captures, blurry text, missing images, complex CSS, and the limits you cannot configure away.

Use the right capture dimensions first

html2canvas renders a DOM element into a browser canvas. The visible viewport is not automatically the whole document, so a page can appear clipped even when the DOM contains more content. Set windowWidth and windowHeight to the target’s scroll dimensions when you need the complete element. Use x, y, width, and height when you intentionally want a region.

Complete element capture

This pattern captures content below the viewport and requests a white background. It also waits for fonts when the browser exposes the Font Loading API.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await document.fonts?.ready;
const target = document.querySelector('#capture');
if (!target) throw new Error('Missing #capture element');

const canvas = await html2canvas(target, {
  scale: window.devicePixelRatio,
  windowWidth: target.scrollWidth,
  windowHeight: target.scrollHeight,
  backgroundColor: '#ffffff',
  useCORS: true,
  logging: true,
  onclone: (clonedDoc) => {
    // Capture-only changes go here.
  }
});

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

Read the dimensions after the layout has settled. If a panel uses an internal scrollbar, its scrollHeight may be larger than its client height; that is exactly the dimension you want for a full panel capture. For a horizontally scrolling region, use scrollWidth as well. Check the computed dimensions in DevTools before changing options.

Capture a deliberate crop

Full-element output can be very large. A crop is more predictable when you know the region that matters:

const canvas = await html2canvas(document.body, {
  x: 120,
  y: 300,
  width: 900,
  height: 600,
  windowWidth: document.documentElement.scrollWidth,
  windowHeight: document.documentElement.scrollHeight,
  scale: 2
});

The coordinates are CSS pixels in the rendered document. Keep the window dimensions large enough to include the crop; otherwise the renderer may still see a viewport-sized layout and omit content outside it.

Make text sharper without exhausting the browser

A canvas is a raster image. Blurry text normally means too few output pixels, not a font-family setting. The documented default for scale is window.devicePixelRatio; setting it explicitly makes the choice clear.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Choose a scale

  • Device-pixel scale: scale: window.devicePixelRatio usually matches the screen’s density.
  • Fixed scale: use 1 for a smaller export, or 2 for a denser image when the target is modest in size.
  • Bounded scale: cap the value on high-density devices to avoid unexpectedly huge canvases, for example Math.min(window.devicePixelRatio || 1, 2).

Doubling scale multiplies each dimension, so the pixel count rises roughly fourfold. Large full-page canvases consume more memory and can hit browser canvas-size limits. If an export fails or the tab becomes unresponsive, reduce scale, capture sections separately, or crop unused whitespace.

Wait for fonts and layout

Call the renderer only after web fonts, images, and late layout changes have completed. document.fonts.ready is a practical precaution, not a universal guarantee: applications that inject styles or data asynchronously must wait for those operations too. A short, targeted delay can help a page with known animation timing, but waiting for a real selector or application-ready event is more reliable.

await document.fonts?.ready;
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
const canvas = await html2canvas(document.querySelector('#capture'), {
  scale: Math.min(window.devicePixelRatio || 1, 2)
});

Fix missing images and cross-origin assets

html2canvas cannot circumvent content-policy restrictions set by the browser. An image hosted on another origin must return a suitable Access-Control-Allow-Origin response header, or it must be fetched through a same-origin proxy that you control.

Direct CORS loading

Use useCORS: true only when the image server is configured for your page’s origin (or an allowed wildcard where appropriate). The browser still enforces the response header; the option does not grant permission.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const canvas = await html2canvas(target, {
  useCORS: true,
  imageTimeout: 15000,
  logging: true
});

The documented imageTimeout default is 15,000 milliseconds. A slow image that exceeds the timeout can be omitted. Increase the timeout only when the page’s loading behavior justifies it; otherwise fix the asset or loading sequence.

Same-origin proxy

When you cannot change the image host, route the resource through a server on your own origin. The html2canvas documentation describes a proxy that accepts a ?url= query and returns the resource as a base64 data URI. Your proxy must validate destination URLs, restrict protocols and prevent server-side request forgery; never expose an unrestricted fetch endpoint.

const canvas = await html2canvas(target, {
  proxy: '/image-proxy?url=',
  useCORS: false
});

allowTaint is not a CORS workaround. A tainted canvas cannot be safely exported with toDataURL() or toBlob(), so enabling it can leave you with an unreadable result rather than a usable screenshot.

Use onclone for capture-only changes

The onclone callback receives the cloned document used for rendering. Modify that copy instead of the live page: replace a clock with a fixed value, reveal a collapsed section, disable an animation, or adjust print-specific styles.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const canvas = await html2canvas(target, {
  onclone: (clonedDoc) => {
    clonedDoc.querySelectorAll('[data-live]').forEach(el => {
      el.textContent = 'Snapshot value';
    });
    const style = clonedDoc.createElement('style');
    style.textContent = `
      *, *::before, *::after { animation: none !important; transition: none !important; }
      .capture-only { display: block !important; }
    `;
    clonedDoc.head.appendChild(style);
  }
});

This avoids flicker and prevents screenshot-specific edits from changing what users see.

Choose between the normal renderer and foreignObjectRendering

The default canvas renderer reconstructs supported HTML and CSS. Set foreignObjectRendering: true to try the browser’s SVG foreign-object path for complex text or CSS. Support and output vary by browser, so test the browsers you actually serve and retain the normal renderer as a fallback.

async function render(target) {
  try {
    return await html2canvas(target, {
      foreignObjectRendering: true,
      scale: window.devicePixelRatio
    });
  } catch (error) {
    console.warn('Foreign-object render failed; using canvas renderer', error);
    return html2canvas(target, {
      foreignObjectRendering: false,
      scale: window.devicePixelRatio
    });
  }
}

Do not assume that a successful render means identical output. Compare line wrapping, filters, pseudo-elements, SVGs and form controls in each target browser.

Exclude unstable or unwanted elements

Remove consent controls, toolbars, cursors or rapidly changing widgets from the clone. Add data-html2canvas-ignore to markup you own, or use ignoreElements for a rule in code.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const canvas = await html2canvas(target, {
  ignoreElements: (element) => element.matches('.cookie-banner, .debug-toolbar')
});

Keep the predicate cheap; it runs while html2canvas walks the document. If an element is needed for layout, hide its visual content rather than removing the node and changing geometry.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Limits you must design around

  • The library runs entirely in the browser; it is not server rendering.
  • Browser content-policy rules still apply to images, fonts and other external resources.
  • Plugin content such as Flash or Java applets is not rendered.
  • Sandboxed iframes without allow-same-origin have important limitations.
  • Canvas dimensions and available memory impose practical limits on very long pages.

For a cross-origin iframe, you generally need cooperation from the framed page and appropriate embedding permissions; html2canvas cannot read arbitrary foreign DOM content.

A systematic troubleshooting order

  1. Verify the target: confirm it is attached to the document and that getBoundingClientRect(), scrollWidth and scrollHeight are non-zero.
  2. Wait for resources: inspect network failures, wait for fonts, images and SVG data, and disable animations in onclone.
  3. Inspect logs: enable logging: true and look for failed image loads or unsupported nodes.
  4. Resolve CORS: add the correct response header or use a secure same-origin proxy; do not rely on allowTaint.
  5. Fix clipping: match windowWidth and windowHeight to scroll dimensions, then use explicit crop coordinates where appropriate.
  6. Improve sharpness: raise scale gradually while watching memory and canvas limits.
  7. Try the alternate path: test foreignObjectRendering for complex CSS, then compare it with the default renderer in each supported browser.
  8. Remove interference: exclude controls or unstable nodes with data-html2canvas-ignore or ignoreElements.

Typical symptoms and fixes

Symptom Likely cause Action
Only the viewport appears Window dimensions are viewport-sized Set both window dimensions from the target’s scroll dimensions.
Text looks soft Low raster scale Use device-pixel scale or a bounded higher value; reduce capture area if memory is tight.
Images are blank CORS failure, timeout or unloaded assets Fix response headers, configure a secure proxy, wait for loading, and inspect logs.
Export throws a security error Tainted canvas Remove unauthorized cross-origin resources or serve them through permitted CORS/proxy paths.
Complex CSS differs Renderer support varies Test foreignObjectRendering and keep a normal-renderer fallback.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. It removes cookie banners, newsletter popups and chat widgets before capture; bot checks, blank pages, failed loads and cache hits are not billed. AI agents can use its MCP tools, including take_screenshot, get_page_info and capture_pdf. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 shots.

One GET request returns PNG, JPEG, WebP or PDF. See the ScreenshotNeo API documentation for all options.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Responses include X-Page-Verdict and X-Billed headers, so you can distinguish a clean billed capture from a failed or non-billable result. Create a free ScreenshotNeo account to start with 1,000 screenshots per month and no card.

Frequently Asked Questions

Can html2canvas capture an entire page automatically?

It can capture the full target when the virtual window is sized to the target’s scroll width and height; the default viewport alone is not a guarantee of full-page output.

Does increasing scale make CSS rendering more accurate?

It increases raster resolution and text sharpness, but it does not add support for CSS or remove cross-origin restrictions.

Should I always enable foreignObjectRendering?

No. It can help with some complex CSS and text, but browser support and output vary; compare it with the default renderer and retain a fallback.

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

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.