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

onrendered no longer runs in current html2canvas releases because that callback was removed. The modern API returns a Promise<HTMLCanvasElement>, so put your code in .then() or use await:

html2canvas(document.querySelector('#capture')).then(canvas => {
  document.body.appendChild(canvas);
});

If that still fails, verify the version your application actually loads, then check Promise errors, cross-origin images, unsupported CSS, and canvas-size limits.

Why onrendered stopped working

Older html2canvas examples (0.4 and earlier) accepted an onrendered option. The project’s rewritten API removed that option as a breaking change. Calling it today does not invoke your callback; html2canvas ignores the unknown option and returns a Promise instead.

The rendering call is asynchronous. This code is therefore wrong even without onrendered:

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.
const canvas = html2canvas(document.querySelector('#capture'));
console.log(canvas.toDataURL()); // canvas is a Promise, not an HTMLCanvasElement

Wait for the Promise to resolve before appending, exporting, reading dimensions, or passing the canvas to another function.

The direct fix: replace the callback with a Promise handler

Using .then()

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

html2canvas(element).then(canvas => {
  document.body.appendChild(canvas);
});

The callback parameter is the completed HTMLCanvasElement. Any operation that needs the rendered image belongs inside this handler.

Using async and await

async function renderCapture() {
  const element = document.querySelector('#capture');
  const canvas = await html2canvas(element);
  document.body.appendChild(canvas);
}

renderCapture();

Use await only inside an async function. If you need to handle a failure, surround it with try/catch.

Exporting the result

async function downloadCapture() {
  const element = document.querySelector('#capture');
  const canvas = await html2canvas(element);
  const imageUrl = canvas.toDataURL('image/png');

  const link = document.createElement('a');
  link.href = imageUrl;
  link.download = 'capture.png';
  link.click();
}

downloadCapture();

Calling toDataURL() before the Promise resolves is a common reason migration attempts fail.

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

Confirm which html2canvas version is loaded

First inspect the dependency that builds your application, not just the example code you copied. Legacy snippets can remain in documentation, blog posts, or old internal components after the project has moved to the Promise-based API.

  1. Run npm ls html2canvas (or inspect the equivalent dependency entry in your package manager).
  2. Check the lockfile to see the resolved version, especially when multiple packages can install html2canvas.
  3. Inspect the browser’s loaded JavaScript bundle or Network panel if a CDN script or separately bundled copy may be overriding your dependency.
  4. Make sure the code path you are testing imports the same copy whose version you checked.

If your application deliberately uses a 0.4-era build, its callback-based examples may still match that build. For the rewritten API and current releases, migrate to the Promise pattern rather than adding another onrendered option.

Make failures visible instead of silently losing them

Promise rejection handling

html2canvas(document.querySelector('#capture'))
  .then(canvas => {
    document.body.appendChild(canvas);
  })
  .catch(error => {
    console.error('html2canvas failed:', error);
  });

try/catch with await

async function renderSafely() {
  try {
    const canvas = await html2canvas(document.querySelector('#capture'));
    return canvas;
  } catch (error) {
    console.error('Unable to render the element:', error);
    return null;
  }
}

renderSafely();

Read the browser console when the rejection occurs. A missing callback and a rejected Promise are different problems: the first is an API-version mismatch, while the second usually involves an image, browser security policy, unsupported content, or resource limits.

Cross-origin images can break export even after the callback fix

html2canvas reconstructs a canvas from the DOM. Images loaded from another origin are subject to the browser’s same-origin and CORS rules. The Promise can resolve while the canvas is unusable for reading, or an external image can be omitted, depending on the resource and response headers.

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

Check the failing resource

  • Open the browser console and identify the image or font request that reports a CORS or security error.
  • Verify that the image server sends an appropriate Access-Control-Allow-Origin response for your page.
  • Use URLs that are actually reachable from the browser, rather than URLs that work only from your server.
  • Test the page with external images removed. If the capture then works, the problem is resource policy rather than onrendered.

Ask html2canvas to attempt CORS loading

html2canvas(document.querySelector('#capture'), {
  useCORS: true
}).then(canvas => {
  document.body.appendChild(canvas);
});

useCORS requests CORS-enabled loading; it cannot override a server that does not grant access. A proxy option is also available for cross-origin images when your deployment provides a suitable proxy. Browser policy still controls whether the resulting canvas can be read.

Do not expect a pixel-perfect browser screenshot

html2canvas creates a representation from DOM information; it does not take a screenshot of the browser’s already-composited pixels. CSS support is selective, and every CSS property must be implemented individually. Unsupported or partially supported properties can produce a result that differs from what you see on screen.

When the Promise resolves but the image looks wrong:

  • Compare the styles used by the target element with html2canvas’s supported-features documentation.
  • Temporarily remove filters, complex blend modes, unusual backgrounds, or other effects that may have limited support.
  • Capture a smaller, simpler element to isolate the first unsupported style.
  • Confirm that web fonts and external images finished loading before starting the capture.

A successful Promise means the rendering operation completed; it does not promise full CSS fidelity.

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.

Fix blank, clipped, or unexpectedly small canvases

Render the element’s complete scroll area

If a long element is clipped, provide dimensions based on its scrollable content:

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

html2canvas(element, {
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight
}).then(canvas => {
  document.body.appendChild(canvas);
});

Use this when the visible viewport is smaller than the element’s full content. It does not remove browser canvas limits.

Check browser canvas limits

Maximum dimensions vary by browser, operating system, hardware, and device memory. The html2canvas FAQ lists these implementation limits as guidance, not universal guarantees:

Environment Maximum width/height listed Maximum area listed
Chrome 32,767 pixels 268,435,456 pixels
Firefox 32,767 pixels 472,907,776 pixels
Internet Explorer 8,192 pixels Not stated
iOS devices with less than 256 MB RAM Not stated 3 megapixels
iOS devices with at least 256 MB RAM Not stated 5 megapixels

These figures can change with the target browser and device. If a large capture is blank or truncated, reduce the capture area, render sections separately, or verify limits on the browsers you support.

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

A practical troubleshooting sequence

  1. Identify the API generation. Confirm the resolved html2canvas version and remove the obsolete onrendered option.
  2. Wait for completion. Move append, export, and post-processing code into .then(canvas => ...) or after await html2canvas(...).
  3. Log rejection details. Add .catch() or try/catch and inspect the console.
  4. Test same-origin content. Temporarily remove remote images and canvases to separate CORS failures from rendering failures.
  5. Check fidelity. Compare the target CSS with supported features and simplify unsupported effects.
  6. Check dimensions. Inspect canvas.width and canvas.height; use scrollWidth/scrollHeight for full content and stay below device limits.
  7. Retest the smallest case. Capture a plain element containing local text and a solid background, then add images and styling back one piece at a time.

Common symptoms and precise fixes

Symptom Likely cause Fix
Code inside onrendered never runs The option was removed from the loaded API. Use the returned Promise with .then() or await.
toDataURL is not a function You called it on the unresolved Promise. Call it on canvas inside the resolved handler.
Remote images are missing or export fails CORS or same-origin restrictions. Configure image response headers, try useCORS, or use a properly configured proxy.
Styles differ from the page The CSS property is unsupported or partially supported. Check supported features and simplify or replace the affected styling.
Large capture is blank or clipped Viewport sizing or browser canvas limits. Set window dimensions from scroll dimensions, reduce the area, and test device-specific limits.

Or skip the browser setup

If your goal is a clean website image rather than a DOM reconstruction inside the user’s browser, ScreenshotNeo makes one server request and returns a PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and lets you turn those steps off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; the response identifies the result with X-Page-Verdict and X-Billed headers.

Use the API documentation at https://screenshotneo.com/docs/ for all parameters. A basic cURL request is:

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

ScreenshotNeo also supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, device presets or custom viewports, retina scale, PDF paper and page-range controls, custom CSS and JavaScript, pre-capture clicks, hidden selectors, selector/delay/network-idle waits, request and resource blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. Its parameter names are compatible with those used by many screenshot APIs, which can simplify migration.

An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots, and every feature is available on every plan. Create a free ScreenshotNeo account.

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.