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

To replace an html2canvas result, remove only the canvas your application previously appended, wait for the next html2canvas() Promise to resolve, then insert the new canvas in the same host. The library returns a new HTMLCanvasElement; it does not decide where that element belongs in your page. Keep a reference or mark generated canvases so cleanup is scoped, and protect asynchronous renders from completing out of order.

What html2canvas creates—and what it does not remove

html2canvas(element, options) renders asynchronously and returns a Promise that resolves to an HTMLCanvasElement. The usual getting-started pattern appends that returned node to document.body. Appending is therefore your code’s responsibility, and replacing or deleting the displayed result is also your responsibility.

During rendering, html2canvas creates temporary cloned DOM elements. The removeContainer option (whose default is true) controls cleanup of those temporary clones after rendering. It does not remove a canvas that you appended to the document. A visible output canvas remains until your code removes it, replaces it, or its containing host is otherwise cleared.

Basic replacement: keep the previous canvas reference

A dedicated host and one variable are the simplest reliable arrangement. The following function removes the prior output only after a new render succeeds:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const host = document.querySelector('#preview');
let previousCanvas = null;

async function replacePreview(element) {
  const nextCanvas = await html2canvas(element);

  if (previousCanvas?.isConnected) {
    previousCanvas.remove();
  }

  host.append(nextCanvas);
  previousCanvas = nextCanvas;
}

isConnected avoids errors when another part of your UI has already detached the old node. Removing after the Promise resolves also means a failed capture does not erase a working preview. If you want the new canvas to occupy exactly the old node’s position, append it first and then remove the old node, or use replaceWith() as shown below.

Replace in place

async function replacePreviewInPlace(element) {
  const nextCanvas = await html2canvas(element);

  if (previousCanvas?.isConnected) {
    previousCanvas.replaceWith(nextCanvas);
  } else {
    host.append(nextCanvas);
  }

  previousCanvas = nextCanvas;
}

replaceWith() preserves the old canvas’s position in its parent. It does not preserve the old canvas’s bitmap or object identity: the node is replaced with the newly returned element.

Remove a previous canvas by marker instead of a variable

Use a marker when components can mount more than once, when state may be recreated, or when another function needs to perform cleanup. Scope the query to the output host and mark only canvases produced for that feature:

const host = document.querySelector('#preview');

async function renderMarkedPreview(source) {
  host.querySelector('canvas[data-html2canvas-output]')?.remove();

  const next = await html2canvas(source);
  next.dataset.html2canvasOutput = 'true';
  host.append(next);
}

function clearPreview() {
  host.querySelector('canvas[data-html2canvas-output]')?.remove();
}

Removing the old node before starting the next capture can leave the UI empty if rendering fails. For a less disruptive update, render first, then remove and append:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
async function renderMarkedPreviewSafely(source) {
  const next = await html2canvas(source);
  next.dataset.html2canvasOutput = 'true';

  host.querySelector('canvas[data-html2canvas-output]')?.remove();
  host.append(next);
}

Prevent an older asynchronous render from winning

Two calls can overlap when a user changes filters quickly or a component rerenders. The first call may finish after the second and overwrite the newer image. html2canvas documents the Promise result, but it does not document cancellation. You can ignore stale completions with a serial number:

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
const host = document.querySelector('#preview');
let previousCanvas = null;
let renderSerial = 0;

async function replacePreviewLatest(element) {
  const serial = ++renderSerial;
  const nextCanvas = await html2canvas(element);

  if (serial !== renderSerial) {
    // A newer capture started; discard this stale result.
    return;
  }

  if (previousCanvas?.isConnected) previousCanvas.remove();
  host.append(nextCanvas);
  previousCanvas = nextCanvas;
}

The serial guard does not stop browser work already in progress; it prevents obsolete output from being committed. When practical, serialize captures instead:

let captureQueue = Promise.resolve();

function queuePreview(element) {
  captureQueue = captureQueue.then(() => replacePreviewLatest(element));
  return captureQueue;
}

For a live preview, debounce the event that calls queuePreview so a burst of input produces fewer captures. A guard is still useful because navigation or component teardown can happen while a Promise is pending.

Reuse an existing canvas when node identity matters

The configuration includes a canvas option: pass an existing, application-owned canvas to use as the drawing base.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const output = document.querySelector('#previewCanvas');

async function redraw(source) {
  await html2canvas(source, { canvas: output });
}

This approach keeps the same DOM node, which can simplify references, event wiring, layout, or code that hands the canvas to another API. It changes the drawing target; it does not make html2canvas synchronous, and it does not remove unrelated canvases. If stable identity is unnecessary, accepting the returned canvas and replacing your old output is usually simpler.

Do not delete unrelated canvases

A broad operation such as document.querySelectorAll('canvas') can destroy charts, signature pads, games, editors, or other application state. Prefer one of these boundaries:

  • Dedicated host: clear only #preview or another container owned by the feature.
  • Marker: remove canvas.html2canvas-output or a data-html2canvas-output attribute.
  • Reference: remove the exact node stored after the prior render.

If a host should contain only one result, set its content deliberately rather than scanning the whole document:

async function renderIntoExclusiveHost(source, host) {
  const next = await html2canvas(source);
  host.replaceChildren(next);
}

replaceChildren() removes every child in that host, so use it only when the host contains no controls or other content you need to keep.

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

Cross-origin images and unreadable output

html2canvas reconstructs a page from DOM and styles in the browser; it is not a pixel-perfect native screenshot engine. Images loaded from another origin can taint the canvas under browser security rules. A canvas may appear correctly rendered but then fail when code calls toDataURL(), toBlob(), or reads pixels.

Choose the documented controls according to the asset origin and your goal:

  • useCORS: true requests CORS-enabled image loading. The image server must send an appropriate CORS header.
  • proxy routes image retrieval through a server you control that can provide permitted, readable responses.
  • allowTaint: true permits tainted content to be drawn, but a tainted bitmap remains unreadable to protected pixel-export APIs.
const next = await html2canvas(source, {
  useCORS: true,
  // proxy: 'https://your.example/image-proxy',
  // allowTaint: true
});

These settings affect bitmap readability, not replacement logic. You still own the output node and must remove or replace it using the same scoped pattern.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Common failure modes and fixes

“A new canvas appears every time”

Cause: each Promise result is appended without removing or replacing the previous result. Fix: retain the previous reference, add a marker, or use a dedicated host with replaceChildren(next).

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.

“removeContainer did not remove my canvas”

Cause: removeContainer cleans html2canvas’s temporary cloned DOM, not a canvas your application appended. Fix: remove the appended output explicitly.

“My chart disappeared”

Cause: cleanup used a document-wide canvas selector. Fix: scope selection to the preview host or a feature-specific marker.

“An old screenshot replaces a newer one”

Cause: overlapping Promises completed out of order. Fix: serialize captures or compare a serial/token before committing the result.

“The canvas is visible but export throws a security error”

Cause: a cross-origin image tainted the bitmap. Fix: configure useCORS with a cooperating image server, use an appropriate proxy, or accept that a tainted canvas cannot be read by export APIs.

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

“The preview is blank after a failed capture”

Cause: code removed the old output before awaiting the new render. Fix: await the new canvas first, then commit it; keep the prior node if rendering rejects.

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

Or skip the browser setup

For server-side or automated captures, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and each response reports the result in X-Page-Verdict and X-Billed headers.

One GET request returns PNG, JPEG, WebP, or a PDF. See the parameter reference and options in the ScreenshotNeo documentation.

cURL

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 bytes = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', bytes);

ScreenshotNeo also supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets and custom viewports, retina scale, PDF paper settings and page ranges, custom CSS or JavaScript, pre-capture clicks, selector hiding, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

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

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to try it.

Choosing the right replacement pattern

Need Recommended pattern Reason
One preview that changes over time Reference plus serial guard Removes only your output and rejects stale completions.
Component remounts or state is recreated Scoped data marker Cleanup can find the prior node without relying on an old variable.
Other children must remain in the host Marker or exact reference Avoids clearing controls and unrelated content.
Consumers require one permanent DOM node canvas option Redraws an application-owned canvas while preserving node identity.
Automated capture outside a browser UI ScreenshotNeo API or MCP Moves navigation, cleanup, waiting, and capture to a service or agent.

Frequently Asked Questions

Can I call html2canvas with the same source element repeatedly?

Yes. Each call is asynchronous and may produce a new canvas unless you provide an existing canvas through the canvas option. Manage the resulting node explicitly.

Should I set removeContainer to false to keep the screenshot?

No. That option concerns html2canvas’s temporary cloned DOM. Keeping an output requires retaining the returned canvas or supplying your own canvas element.

Does replacing a canvas free every resource immediately?

Removing the DOM node detaches it, but release references you no longer need so garbage collection can reclaim it. Also clear application timers, listeners, or other objects that refer to the old canvas.

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.

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.