Modern Copy, Cut, and Paste on the Web
If you’ve ever needed to copy, cut, or paste content in a web app, the Clipboard API is the modern way to do it—no more relying on deprecated document.execCommand(). Available in secure contexts, it lets you asynchronously read from and write to the system clipboard, and respond to user clipboard actions via events.
What it offers
- Async read/write: Use Navigator.clipboard to read and write text (and other data types via ClipboardItem) without blocking the UI.
- Events: Handle copy, cut, and paste events to customize behavior or intercept default actions.
- Broader formats: Beyond plain text, ClipboardItem enables richer data workflows.
Security and permissions
Clipboard access is sensitive. Browsers gate it behind permissions and user gestures:
- Secure contexts only (HTTPS).
- Reading: Spec requires transient user activation; browsers differ. Chromium uses the Permissions API (clipboard-read). Firefox and Safari require transient activation and don’t support clipboard-read/clipboard-write permissions.
- Writing: Typically needs transient activation or clipboard-write permission (Chromium). Iframes may need Permissions-Policy allowances. Policy and prompts vary by browser.
Clipboard API Demo
Clipboard API Demo
Writing to the Clipboard
This is the text that will be copied to your clipboard when you click the button.
Reading from the Clipboard
Click the button below to paste content from your clipboard into the text area.
Status: Ready
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
line-height: 1.6;
background-color: #f4f7f9;
color: #333;
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
}
h1,
h2 {
color: #005a9c;
}
.demo-section {
background-color: #fff;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 2rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
#text-to-copy {
background-color: #eef5ff;
padding: 1rem;
border-radius: 4px;
border: 1px dashed #b3d4fc;
}
button {
background-color: #007bff;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.2s;
margin-top: 10px;
}
button:hover {
background-color: #0056b3;
}
textarea {
width: 95%;
min-height: 100px;
padding: 10px;
border-radius: 5px;
border: 1px solid #ccc;
font-size: 1rem;
margin-top: 10px;
display: block;
}
.status {
text-align: center;
color: #555;
font-style: italic;
}
// Wait for the DOM to be fully loaded before running our script
document.addEventListener('DOMContentLoaded', () => {
// Select all the elements we'll need to interact with
const copyBtn = document.querySelector('#copy-btn');
const textToCopy = document.querySelector('#text-to-copy');
const pasteBtn = document.querySelector('#paste-btn');
const pasteArea = document.querySelector('#paste-area');
const statusMessage = document.querySelector('#status-message');
// --- WRITE TO CLIPBOARD ---
copyBtn.addEventListener('click', async () => {
// First, check if the Clipboard API is available in the browser
if (!navigator.clipboard) {
statusMessage.textContent =
'Clipboard API not supported by your browser.';
return;
}
try {
// Get the text from our paragraph element
const text = textToCopy.innerText;
// Use the writeText() method to copy the text.
// This is an asynchronous operation, so we use await.
await navigator.clipboard.writeText(text);
// Update the status message to give the user feedback
statusMessage.textContent = 'Text copied to clipboard!';
console.log('Text successfully copied.');
} catch (err) {
// If there's an error, log it to the console and update the status
statusMessage.textContent = 'Failed to copy text.';
console.error('Failed to copy text: ', err);
}
});
// --- READ FROM CLIPBOARD ---
pasteBtn.addEventListener('click', async () => {
if (!navigator.clipboard) {
statusMessage.textContent =
'Clipboard API not supported by your browser.';
return;
}
try {
// Use the readText() method to get content from the clipboard.
// This is also asynchronous.
// The browser will likely ask the user for permission here for security.
const text = await navigator.clipboard.readText();
// Set the value of the textarea to the text we just read
pasteArea.value = text;
// Provide user feedback
statusMessage.textContent = 'Text pasted from clipboard!';
console.log('Text successfully pasted.');
} catch (err) {
// Handle any errors, such as the user denying permission
statusMessage.textContent = 'Failed to paste text.';
console.error('Failed to read from clipboard: ', err);
}
});
});
Key interfaces
- Clipboard: Async readText(), writeText(), and richer read()/write() for multiple formats.
- ClipboardEvent: Data for cut, copy, paste events.
- ClipboardItem: Represents individual items and formats when handling non-text data.
Gotchas and best practices
- Always assume user interaction is required; tie reads/writes to clicks or key presses.
- Handle rejections gracefully—failures can stem from permissions, policies, or cross-origin content.
- Consider browser differences: transient activation rules and Permissions API support vary; test in Chromium, Firefox, and Safari.
- In iframes on Chromium, set Permissions-Policy for clipboard-read and clipboard-write.
Why use it
The Clipboard API delivers a secure, user-friendly, and flexible approach to clipboard operations, aligning with modern web security models and enabling richer interactions than legacy methods. If your app involves editing, sharing, or quick data transfer, it’s the right tool for the job.