JavaScript Async Clipboard API for Rich Data
The modern JavaScript Asynchronous Clipboard API allows web
applications to read and write complex, multi-format system
data—including plain text, HTML, and binary images—without blocking the
main thread. By utilizing navigator.clipboard alongside
ClipboardItem and Blob objects, developers can
seamlessly exchange rich data between the web platform and the native
operating system clipboard under secure browser contexts.
Core Architecture and Security Requirements
The asynchronous Clipboard API is accessed through the
navigator.clipboard object. Because accessing system
clipboard data involves privacy considerations, the API enforces
specific security rules: - It requires a Secure Context
(HTTPS or localhost). - Writing and reading operations typically require
transient user activation (such as a click event). -
Reading non-text or rich content requires explicit permission granted
via the Permissions API (clipboard-read).
The ClipboardItem
Interface
Unlike legacy synchronous commands
(document.execCommand('copy')), which were limited
primarily to plain text, the Async Clipboard API uses the
ClipboardItem interface to handle arbitrary MIME types. A
single ClipboardItem can hold multiple representations of
the same data (e.g., both text/html and
text/plain), allowing receiving applications to choose the
format they support best.
Writing Rich Data to the Clipboard
To write rich data, you create one or more Blob objects
representing the content types, pass them into a
ClipboardItem, and call
navigator.clipboard.write().
async function copyRichData() {
try {
const htmlContent = "<strong>Formatted</strong> text to copy";
const plainContent = "Formatted text to copy";
const htmlBlob = new Blob([htmlContent], { type: "text/html" });
const textBlob = new Blob([plainContent], { type: "text/plain" });
const clipboardItem = new ClipboardItem({
"text/html": htmlBlob,
"text/plain": textBlob
});
await navigator.clipboard.write([clipboardItem]);
console.log("Rich content copied successfully.");
} catch (err) {
console.error("Failed to copy rich data:", err);
}
}To write binary data such as images, provide an image blob (e.g.,
image/png):
async function copyImage(blob) {
const item = new ClipboardItem({ [blob.type]: blob });
await navigator.clipboard.write([item]);
}Reading Rich Data from the Clipboard
Reading rich data from the system clipboard is performed using
navigator.clipboard.read(), which resolves to an array of
ClipboardItem objects. You can inspect each item’s
types array to detect available formats and retrieve the
corresponding Blob using getType().
async function readRichData() {
try {
const items = await navigator.clipboard.read();
for (const item of items) {
if (item.types.includes("text/html")) {
const blob = await item.getType("text/html");
const html = await blob.text();
console.log("HTML Data:", html);
} else if (item.types.includes("image/png")) {
const blob = await item.getType("image/png");
const imgUrl = URL.createObjectURL(blob);
console.log("Image URL:", imgUrl);
} else if (item.types.includes("text/plain")) {
const blob = await item.getType("text/plain");
const text = await blob.text();
console.log("Plain Text:", text);
}
}
} catch (err) {
console.error("Failed to read clipboard:", err);
}
}Data Sanitization and Browser Constraints
When transferring rich formats like text/html, modern
browsers automatically sanitize markup to strip malicious scripts before
putting it on or reading it from the system clipboard. For custom
formats not recognized by default MIME handlers, browsers are
implementing web custom formats, enabling arbitrary data serialization
across web applications.