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:
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
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:
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
- 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.
Recommended Free Tools
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:
Rank #3
- Dedicated host: clear only
#previewor another container owned by the feature. - Marker: remove
canvas.html2canvas-outputor adata-html2canvas-outputattribute. - 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.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsCross-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: truerequests CORS-enabled image loading. The image server must send an appropriate CORS header.proxyroutes image retrieval through a server you control that can provide permitted, readable responses.allowTaint: truepermits 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
- 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.
“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.
Best Value
“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.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.
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.
Quick Recap
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.

