Clipboard API
Copy text and rich content to and from the clipboard using the modern async Clipboard API and handle paste events for custom input behavior.
Overview
Key Points
- •`navigator.clipboard.writeText(str)` write text to clipboard (requires secure context: HTTPS or localhost)
- •`navigator.clipboard.readText()` read text from clipboard (requires "clipboard-read" permission)
- •`ClipboardItem` enables copying rich content (images, HTML) alongside plain text
- •The `copy`, `cut`, `paste` events fire on editable elements; access data via `event.clipboardData`
- •`event.clipboardData.setData(type, data)` in a copy event lets you customize what gets copied
- •`event.clipboardData.getData(type)` in a paste event reads the clipboard before insertion
- •Clipboard read access requires user permission browsers may show a prompt
Examples
Copy to clipboard with user feedback
<button id="copy-btn" data-copy="npm install react">
Copy Install Command
</button>
<script>
document.getElementById('copy-btn').addEventListener('click', async (e) => {
const text = e.currentTarget.dataset.copy;
try {
await navigator.clipboard.writeText(text);
// Provide visual feedback
const btn = e.currentTarget;
const original = btn.textContent;
btn.textContent = '✓ Copied!';
btn.disabled = true;
setTimeout(() => {
btn.textContent = original;
btn.disabled = false;
}, 2000);
} catch (err) {
console.error('Copy failed:', err);
// Fallback for older browsers
fallbackCopy(text);
}
});
function fallbackCopy(text) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
}
</script>Always provide visual feedback after copying users need confirmation that the action worked.
Intercepting the paste event to sanitize input
<div
id="rich-editor"
contenteditable="true"
role="textbox"
aria-multiline="true"
>Type or paste here...</div>
<script>
document.getElementById('rich-editor').addEventListener('paste', (event) => {
event.preventDefault();
// Get pasted text, stripping all HTML/formatting
const text = event.clipboardData.getData('text/plain');
// Insert as plain text at cursor position
document.execCommand('insertText', false, text);
// OR in modern browsers:
// document.getSelection().getRangeAt(0).insertNode(document.createTextNode(text));
});
</script>Preventing the default paste and inserting only plain text is a common pattern for rich text editors that should not accept formatted paste.
Copying rich content (image + text) with ClipboardItem
<img id="chart" src="chart.png" alt="Sales chart">
<button id="copy-image">Copy Chart</button>
<script>
document.getElementById('copy-image').addEventListener('click', async () => {
const img = document.getElementById('chart');
try {
// Fetch the image as a Blob
const response = await fetch(img.src);
const blob = await response.blob();
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob, // image/png
'text/plain': new Blob(
['Sales Chart Q4 2024'],
{ type: 'text/plain' }
),
})
]);
alert('Chart copied to clipboard!');
} catch (err) {
console.error('Rich copy failed:', err);
}
});
</script>ClipboardItem lets you provide multiple MIME types the recipient's app picks the richest format it supports.
Common Mistakes
- Using the Clipboard API outside a user gesture browsers require a user action (click, keydown) to access the clipboard
- Not handling the permission error when `readText()` is called users can deny clipboard-read permission
- Forgetting the secure context requirement the Clipboard API only works on HTTPS or localhost
Note
The legacy `document.execCommand("copy")` is deprecated but still useful as a fallback. Always try the modern Clipboard API first, then fall back to execCommand in a catch block.
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4