|$ curl https://forge-ai.dev/api/markdown?path=docs/js/clipboard-api
$cat docs/clipboard-api.md
updated Last week·18 min read·published

Clipboard API

JavaScriptWeb APIDOMIntermediate🎯Free Tools
Introduction

The Clipboard API provides programmatic access to the system clipboard. It allows you to read and write text, HTML, images, and other data types to and from the clipboard. The API is part of the W3C Clipboard API specification and is available in all modern browsers.

Unlike the older document.execCommand('copy') approach, the Clipboard API is Promise-based, works asynchronously, and requires explicit user permission. It is the recommended way to handle clipboard operations in modern web applications.

clipboard-intro.js
JavaScript
1// Clipboard API overview
2// Write to clipboard
3await navigator.clipboard.writeText("Hello, clipboard!");
4
5// Read from clipboard
6const text = await navigator.clipboard.readText();
7console.log(text); // "Hello, clipboard!"
8
9// Check if Clipboard API is available
10if (navigator.clipboard) {
11 console.log("Clipboard API available");
12} else {
13 console.log("Clipboard API not supported");
14}
writeText() — Copying Text

The writeText() method copies a string to the clipboard. It returns a Promise that resolves when the copy operation is complete. The method requires the page to be focused and the user to have triggered the action via a user gesture (click, keydown, etc.).

clipboard-write.js
JavaScript
1// Basic copy to clipboard
2async function copyText(text) {
3 try {
4 await navigator.clipboard.writeText(text);
5 console.log("Copied to clipboard!");
6 return true;
7 } catch (err) {
8 console.error("Failed to copy:", err);
9 return false;
10 }
11}
12
13// Usage in a click handler
14document.getElementById("copy-btn").addEventListener("click", async () => {
15 const success = await copyText("Hello, World!");
16 if (success) {
17 showNotification("Copied!");
18 }
19});
20
21// Copy code block content
22document.querySelectorAll("pre code").forEach((codeBlock) => {
23 codeBlock.addEventListener("click", async () => {
24 const text = codeBlock.textContent;
25 try {
26 await navigator.clipboard.writeText(text);
27 codeBlock.classList.add("copied");
28 setTimeout(() => codeBlock.classList.remove("copied"), 2000);
29 } catch (err) {
30 console.error("Copy failed:", err);
31 }
32 });
33});
34
35// Copy with feedback
36async function copyWithFeedback(text, button) {
37 const originalText = button.textContent;
38 try {
39 await navigator.clipboard.writeText(text);
40 button.textContent = "Copied!";
41 button.disabled = true;
42 setTimeout(() => {
43 button.textContent = originalText;
44 button.disabled = false;
45 }, 2000);
46 } catch (err) {
47 button.textContent = "Failed!";
48 setTimeout(() => {
49 button.textContent = originalText;
50 }, 2000);
51 }
52}
readText() — Pasting Text

The readText() method reads text from the clipboard. It requires explicit user permission — the browser will show a permission prompt if the user has not already granted access. This method requires the page to be focused.

clipboard-read.js
JavaScript
1// Read text from clipboard
2async function pasteText() {
3 try {
4 const text = await navigator.clipboard.readText();
5 console.log("Pasted:", text);
6 return text;
7 } catch (err) {
8 if (err.name === "NotAllowedError") {
9 console.log("Permission denied — user may need to grant clipboard access");
10 } else {
11 console.error("Paste failed:", err);
12 }
13 return null;
14 }
15}
16
17// Paste into an input field
18document.getElementById("paste-btn").addEventListener("click", async () => {
19 const text = await pasteText();
20 if (text !== null) {
21 document.getElementById("input-field").value = text;
22 }
23});
24
25// Paste on Ctrl+V (modern approach)
26document.addEventListener("keydown", async (e) => {
27 if (e.ctrlKey && e.key === "v") {
28 // Note: this event fires before the native paste
29 // For most cases, let the browser handle the paste event
30 }
31});
32
33// Better: use the paste event
34document.getElementById("text-area").addEventListener("paste", async (e) => {
35 e.preventDefault();
36 try {
37 const text = await navigator.clipboard.readText();
38 document.execCommand("insertText", false, text);
39 } catch (err) {
40 // Fallback: let browser handle it
41 console.log("Custom paste failed, using default behavior");
42 }
43});

info

readText() requires user permission. The first time a page requests clipboard read access, the browser shows a permission prompt. If the user denies permission, subsequent calls will throw NotAllowedError. Always handle this error gracefully.
read() & write() — Rich Content

The read() and write() methods handle multiple data types (text, HTML, images, files) using ClipboardItem objects. They provide more flexibility than the text-only methods.

clipboard-rich.js
JavaScript
1// Read multiple types from clipboard
2async function readClipboard() {
3 try {
4 const clipboardItems = await navigator.clipboard.read();
5
6 for (const item of clipboardItems) {
7 // Check available types
8 for (const type of item.types) {
9 console.log("Available type:", type);
10
11 if (type === "text/plain") {
12 const blob = await item.getType(type);
13 const text = await blob.text();
14 console.log("Text:", text);
15 }
16
17 if (type === "text/html") {
18 const blob = await item.getType(type);
19 const html = await blob.text();
20 console.log("HTML:", html);
21 }
22
23 if (type === "image/png") {
24 const blob = await item.getType(type);
25 const url = URL.createObjectURL(blob);
26 console.log("Image URL:", url);
27 // Display the image
28 const img = document.createElement("img");
29 img.src = url;
30 document.body.appendChild(img);
31 }
32 }
33 }
34 } catch (err) {
35 console.error("Read failed:", err);
36 }
37}
38
39// Write rich content to clipboard
40async function writeRichContent() {
41 const html = "<strong>Bold text</strong> and <em>italic</em>";
42 const plainText = "Bold text and italic";
43
44 const blob = new Blob([html], { type: "text/html" });
45 const textBlob = new Blob([plainText], { type: "text/plain" });
46
47 const item = new ClipboardItem({
48 "text/html": blob,
49 "text/plain": textBlob
50 });
51
52 await navigator.clipboard.write([item]);
53 console.log("Rich content copied!");
54}
Handling Permissions

The Clipboard API requires specific permissions. writeText() works without explicit permission (must be triggered by user gesture). readText() and read() require user permission. You can check permission status using the Permissions API.

clipboard-permissions.js
JavaScript
1// Check clipboard read permission
2async function checkClipboardPermission() {
3 try {
4 const result = await navigator.permissions.query({
5 name: "clipboard-read"
6 });
7
8 console.log("Permission state:", result.state);
9 // "granted", "denied", or "prompt"
10
11 if (result.state === "granted") {
12 const text = await navigator.clipboard.readText();
13 console.log("Clipboard:", text);
14 } else if (result.state === "prompt") {
15 // Will prompt user on next read attempt
16 console.log("User will be prompted for permission");
17 } else {
18 console.log("Clipboard access denied");
19 }
20 } catch (err) {
21 // Permissions API may not support clipboard-read
22 console.log("Cannot check permission:", err.message);
23 }
24}
25
26// Safe clipboard read with fallback
27async function safeReadClipboard() {
28 try {
29 const text = await navigator.clipboard.readText();
30 return text;
31 } catch (err) {
32 if (err.name === "NotAllowedError") {
33 console.log("Clipboard permission denied — showing manual paste UI");
34 showManualPasteDialog();
35 } else if (err.name === "NotFoundError") {
36 console.log("Clipboard is empty");
37 return "";
38 } else {
39 throw err;
40 }
41 }
42}
43
44function showManualPasteDialog() {
45 const dialog = document.createElement("div");
46 dialog.innerHTML = `
47 <p>Press Ctrl+V (Cmd+V) to paste:</p>
48 <textarea id="manual-paste" rows="4"></textarea>
49 <button id="manual-paste-ok">OK</button>
50 `;
51 document.body.appendChild(dialog);
52}
Fallback Strategies

Not all browsers support the Clipboard API, and some environments (like iframes) restrict clipboard access. Always provide fallback strategies using the older document.execCommand() approach or manual paste prompts.

clipboard-fallback.js
JavaScript
1// Fallback using execCommand (deprecated but widely supported)
2function copyTextFallback(text) {
3 const textarea = document.createElement("textarea");
4 textarea.value = text;
5 textarea.style.position = "fixed";
6 textarea.style.left = "-9999px";
7 textarea.style.top = "-9999px";
8 document.body.appendChild(textarea);
9 textarea.focus();
10 textarea.select();
11
12 try {
13 const success = document.execCommand("copy");
14 document.body.removeChild(textarea);
15 return success;
16 } catch (err) {
17 document.body.removeChild(textarea);
18 return false;
19 }
20}
21
22// Modern API with fallback
23async function copyTextSmart(text) {
24 // Try modern API first
25 if (navigator.clipboard && window.isSecureContext) {
26 try {
27 await navigator.clipboard.writeText(text);
28 return true;
29 } catch (err) {
30 console.warn("Modern clipboard failed, falling back:", err);
31 }
32 }
33
34 // Fallback to execCommand
35 return copyTextFallback(text);
36}
37
38// Paste event handler (always works — no permission needed)
39document.addEventListener("paste", (e) => {
40 const text = e.clipboardData?.getData("text/plain");
41 if (text) {
42 console.log("Pasted:", text);
43 }
44});
45
46// Copy event handler (for intercepting Ctrl+C)
47document.addEventListener("copy", (e) => {
48 const selection = window.getSelection()?.toString();
49 if (selection) {
50 console.log("User copied:", selection);
51 // Let the default copy happen, or override with:
52 // e.clipboardData.setData("text/plain", customText);
53 // e.preventDefault();
54 }
55});

best practice

Always check for navigator.clipboard availability and window.isSecureContext before using the Clipboard API. Fall back to document.execCommand('copy') for older browsers. For paste operations, the paste event is the most reliable approach since it works without explicit permission.
Best Practices
Always wrap clipboard operations in try/catch — they can fail for many reasons
Only use clipboard.readText() in response to user gestures — otherwise permission will be denied
Show visual feedback when content is copied — users need confirmation the action worked
Provide fallback for older browsers using document.execCommand('copy')
Use paste event for intercepting paste operations — it works without permission
Never silently read clipboard content without user knowledge — this is a privacy violation
Check window.isSecureContext before using Clipboard API — it requires HTTPS
For rich content, use write() with ClipboardItem — supports text, HTML, and images
$Blueprint — Engineering Documentation·Section ID: JS-CLIP·Revision: 1.0