How to Copy a Div to the Clipboard as an Image
Render a DOM element to PNG, then copy it with the Clipboard API. Learn the full browser workflow, its limits, and a simpler screenshot API option.
To copy a rendered <div> as an image, first render it to a canvas, convert that canvas to a PNG Blob, then write the blob with the Async Clipboard API. The Clipboard API does not turn DOM into pixels: a renderer such as html2canvas handles that separate step. Run the operation from a user action on a focused page served over HTTPS, and handle both rendering and clipboard errors.
How the copy flow works
- Select the element whose visible content you want to copy.
- Render it into a canvas. This reconstructs the DOM as canvas drawing; it is not a native browser screenshot.
- Encode the canvas as a PNG blob with
canvas.toBlob(). - Put the blob in an
image/pngClipboardItemand awaitnavigator.clipboard.write(). - Show success only after the write promise resolves.
PNG is the most practical format to start with: image/png is a mandated clipboard type. A destination application still determines how pasted image data is handled. See MDN’s Clipboard.write() and ClipboardItem references.
Complete browser example with html2canvas
Install html2canvas in your app using the package manager and bundler you already use, or include its browser build according to the getting started guide. The example below assumes html2canvas is available and the page contains an element with id="card" and a button with id="copy-card".
<div id="card">
<h2>Release summary</h2>
<p>The selected element will be copied as a PNG image.</p>
</div>
<button id="copy-card" type="button">Copy card as image</button>
<p id="copy-status" role="status" aria-live="polite"></p>
<script type="module">
async function canvasToPngBlob(canvas) {
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob) resolve(blob);
else reject(new Error("PNG encoding failed"));
}, "image/png");
});
}
async function copyElementAsImage(element) {
if (!element) throw new Error("Target element was not found");
if (!window.isSecureContext || !navigator.clipboard?.write) {
throw new Error("Image clipboard writing is unavailable in this context");
}
const canvas = await html2canvas(element);
const blob = await canvasToPngBlob(canvas);
const item = new ClipboardItem({ "image/png": blob });
await navigator.clipboard.write([item]);
}
const button = document.querySelector("#copy-card");
const status = document.querySelector("#copy-status");
button.addEventListener("click", async () => {
button.disabled = true;
status.textContent = "Preparing image…";
try {
const card = document.querySelector("#card");
await copyElementAsImage(card);
status.textContent = "Image copied. Paste it into your destination.";
} catch (error) {
console.error("Could not copy element as image", error);
status.textContent = error instanceof Error
? `Could not copy image: ${error.message}`
: "Could not copy image. Check browser permissions and try again.";
} finally {
button.disabled = false;
}
});
</script>
Keep the call in the click handler rather than running it during page load. The browser can deny clipboard access, so the promise must be awaited and rejected writes must not be reported as success. The code is a practical pattern; validate it in the browsers and framework lifecycle your application supports.
Configuration and implementation choices
Choose what to render
Pass the actual element reference to html2canvas, not a selector string. If the element is missing, handle that as an application error before attempting the render. For a card that changes immediately before copying, update the UI first and capture after the framework has committed the new render.
Control the capture
html2canvas accepts rendering options. Consult its documentation for the current option list and behavior. Common decisions include whether to use a transparent background, the output scale, and how to handle cross-origin assets. Avoid promising exact fidelity for every CSS feature: this library reconstructs the page as canvas drawing and its output can differ from browser-native rendering.
PNG, dimensions, and memory
PNG is a lossless raster format and works well for text-heavy cards. Canvas dimensions grow with both element size and scale; very large or full-page elements can consume substantial memory, take longer to encode, or exceed browser canvas limits. Capture only the needed element and avoid increasing scale without a display-quality reason.
Alternative: draw a constrained design yourself
If the content has a small, fixed set of shapes and text, drawing directly to canvas can avoid a general DOM renderer. That also means implementing layout, fonts, wrapping, and styles yourself. A DOM-to-canvas library is usually simpler for existing HTML; a custom renderer can be more predictable for a narrowly defined graphic. The available research does not establish one approach as universally superior.
Browser requirements and edge cases
- Secure context and focus: Clipboard access is limited to secure contexts, and the window needs to be focused. Use HTTPS in production and trigger copying from an obvious user action. See MDN Clipboard API.
- Permission and activation:
write()is asynchronous and may reject. Browser activation behavior differs. In particular, web.dev describes differences between Safari/WebKit and Chromium/Blink. Test on the browsers you support; its copy images guide discusses keeping asynchronous image production in a promise value supplied toClipboardItemwhen activation timing requires it. - Cross-origin images: A canvas cannot bypass browser origin protections. External images without suitable CORS permission can taint the canvas, preventing it from being read or encoded. Configure the image host to allow the required CORS access, proxy assets where appropriate, or omit the blocked images. html2canvas documents these origin restrictions.
- Cross-origin iframes: html2canvas cannot render a cross-origin iframe because the browser prevents access to its
contentDocument. Capture content owned by your page or provide a separate image representation. - Font and layout readiness: If fonts or images are still loading, the rendered canvas may not match the settled page. Wait for the content your card depends on before calling the renderer.
- Clipboard destinations: Successful writing means the browser accepted the data. It does not guarantee every target app will paste or preserve the image identically.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
navigator.clipboard is missing |
The page is not in a secure context, the browser lacks the API, or the current context does not expose it. | Use HTTPS, check window.isSecureContext, and provide a clear unsupported-browser message. |
navigator.clipboard.write() rejects |
Permission was denied, the window is not focused, or the browser’s user-activation requirement was not met. | Start from a direct user action, keep the page focused, handle the rejection, and test the relevant browser. Consider the promise-in-ClipboardItem pattern described by web.dev. |
| Canvas export throws a security error | A cross-origin image or other resource tainted the canvas. | Allow the needed CORS access at the resource origin, use an allowed same-origin resource, or leave the asset out of the capture. |
| Part of the image is missing or incorrect | Unsupported or differently rendered CSS, an unloaded font or image, or an inaccessible cross-origin frame. | Wait for assets, simplify the target markup, replace inaccessible frame content with an image or same-origin representation, and compare the library output against the supported design. |
toBlob() returns null |
Canvas encoding did not produce a blob. | Reject the operation as in the example; reduce canvas size or investigate whether the canvas is tainted. |
| Click appears to do nothing | The target selector did not match, an exception was swallowed, or UI state says success before the async operation completes. | Check the selected element, log the error, and update status only after the awaited write resolves. |
| Output is slow or the tab becomes unresponsive | The target is very large or the requested scale creates a large bitmap. | Capture a smaller element, lower the scale, and avoid redundant captures. Keep the interface responsive while the async render runs. |
Performance, reliability, and cost
The browser performs rendering, PNG encoding, and the clipboard write on the user’s device. Time and memory depend on element size, scale, asset loading, browser, and destination. Avoid capturing the same unchanged element repeatedly; if the image is reused, keep a blob or canvas for the relevant UI state and invalidate it when content changes. For reliability, expose progress and failure states, keep the copy action user initiated, and test with the origin and assets used in production.
This approach has no screenshot API request fee, but it carries implementation and maintenance costs: a rendering dependency, browser-specific behavior, origin constraints, and your own testing. It is appropriate when the image must be copied directly from the current page in the user’s browser.
Or skip the browser setup
If you need a screenshot file of a page or element rather than copying the current tab’s DOM directly into the user’s clipboard, ScreenshotNeo is a website screenshot API and MCP server. Its API supports capturing a selected element by CSS selector. A screenshot response is an image or PDF, so an application that needs clipboard paste can fetch the image and still write it through the browser clipboard flow above.
See the ScreenshotNeo API docs. 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)
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}`);
- Cookie banners are accepted and removed before the shot; newsletter popups and chat widgets are removed too.
- Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers say the page verdict and billing status.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account for 1,000 screenshots per month with no card.
FAQ
Can I copy an HTML element directly with the Clipboard API?
No. The Clipboard API writes supported data formats; first rasterize the element and create an image blob.
Can I copy SVG instead of PNG?
Clipboard support for image types beyond PNG is optional. PNG is the dependable starting point for interoperable raster output.
Does this capture the whole page?
The example renders the selected element. To capture a larger region, select a containing element, bearing in mind the added rendering time and canvas size.
Will it work from a server-rendered app?
The capture and clipboard write require a browser window and user interaction. Run the code in a client-side component after the target element exists.


