Using the Clipboard API to copy and paste text in JavaScript
The Clipboard API gives you programmatic access to the system clipboard. You can read text that users have copied and write text you want to let them paste elsewhere. It’s useful for building features like copy-to-clipboard buttons, paste handlers, and clipboard-powered workflows.
This API lives on navigator.clipboard and provides promise-based methods for reading and writing text. It’s supported in all modern browsers, but requires a secure context (HTTPS) in most cases.
What is the Clipboard API
The Clipboard API is a modern browser API that gives JavaScript programmatic access to the system clipboard. It replaces the older document.execCommand('copy') approach with a cleaner, promise-based interface. The API runs in a secure context (HTTPS) and separates reading from writing through two distinct methods: navigator.clipboard.writeText() for copying text and navigator.clipboard.readText() for pasting it. Both methods require user interaction to prevent silent clipboard access, which protects users from malicious pages that might otherwise steal or overwrite clipboard contents.
Before the Clipboard API, developers relied on document.execCommand('copy'), a synchronous and less reliable mechanism that required creating hidden textarea elements and manually selecting text. The modern API is asynchronous, more secure, and integrated with the browser’s permission system.
Copying text to the clipboard
The navigator.clipboard.writeText() method copies a string to the clipboard. Call it on navigator.clipboard:
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('Text copied to clipboard');
} catch (err) {
console.error('Failed to copy:', err);
}
}
// Usage
copyToClipboard('Hello, world!');
The method returns a promise that resolves when the copy succeeds. If the write fails, whether due to permissions, missing user activation, or an insecure context, the promise rejects.
Most browsers require what the spec calls transient user activation for clipboard writes. This means the code must run in response to a user action like a click or a keyboard shortcut—not from a timer, a promise resolution, or the page load event. Without this guard, any script could silently read or overwrite clipboard data without the user knowing. Wrap your copy function behind a button click to satisfy this requirement:
document.querySelector('.copy-btn').addEventListener('click', async () => {
const text = document.querySelector('.code-block').textContent;
await navigator.clipboard.writeText(text);
// Show feedback to user
});
Reading text from the clipboard
The navigator.clipboard.readText() method reads the current clipboard contents as a string. It returns a promise that resolves with the clipboard text, or rejects if the browser denies access. Unlike writing, reading is strictly gated behind user interaction and permission checks in every modern browser:
async function readFromClipboard() {
try {
const text = await navigator.clipboard.readText();
console.log('Clipboard contents:', text);
return text;
} catch (err) {
console.error('Failed to read clipboard:', err);
}
}
Reading is more restricted than writing. Browsers enforce permission prompts or require recent user interaction before allowing a read. Chromium browsers may show a permission prompt for clipboard-read. Firefox and Safari avoid the prompt entirely and instead surface a paste option through an ephemeral context menu. In all cases, the read must originate from a user gesture such as a click or a keyboard shortcut.
This works reliably when triggered by a user action:
document.querySelector('.paste-btn').addEventListener('click', async () => {
const clipboardText = await navigator.clipboard.readText();
document.querySelector('.output').textContent = clipboardText;
});
Handling permissions
The Permissions API lets you check and request clipboard permissions before attempting operations. Querying the permission state upfront helps you decide whether to show a copy button, request access, or display a fallback message. This is especially important for clipboard read operations, where the user may be prompted by the browser. The code below queries clipboard-read permission and handles all three possible states: granted access, denied by the user, or awaiting a prompt response.
async function checkClipboardPermission() {
const permission = await navigator.permissions.query({
name: 'clipboard-read'
});
console.log('Clipboard permission:', permission.state);
// Possible states: 'granted', 'denied', 'prompt'
}
Note that Firefox and Safari don’t support the clipboard permissions yet. They rely on transient user activation instead, which means the browser decides access based on whether the user recently interacted with the page. Your code should handle both cases: check for permission support before querying, and fall back to assuming access is granted when the Permissions API does not cover the clipboard:
async function safeReadClipboard() {
// Check if Permissions API supports clipboard
if ('permissions' in navigator) {
try {
const { state } = await navigator.permissions.query({
name: 'clipboard-read'
});
if (state === 'denied') {
throw new Error('Clipboard access denied');
}
} catch (e) {
// Firefox/Safari don't support this query
console.log('Using fallback activation method');
}
}
return navigator.clipboard.readText();
}
Browser compatibility
The Clipboard API works in all modern browsers:
- Chrome 66+
- Firefox 87+
- Safari 13.1+
- Edge 79+
Key requirements:
- Secure context: Most browsers require HTTPS. The API may be unavailable or throw errors on HTTP.
- User activation: Writing requires the page to be in a state caused by user interaction. Reading has stricter requirements in some browsers.
- iframe restrictions: If your content runs in an iframe, the parent page must allow clipboard access via the
Permissions-Policyheader:
Permissions-Policy: clipboard-read=(self), clipboard-write=(self)
For older browsers or restricted contexts where the Clipboard API is unavailable, fall back to the legacy document.execCommand('copy') approach. It is more cumbersome and browsers are slowly removing it, but it still covers the few remaining environments that lack the modern API:
function fallbackCopy(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
Use cases
The Clipboard API enables several common patterns in web applications:
- Copy buttons: Add one-click copy for code snippets, discount codes, or referral links
- Paste handlers: Read clipboard content when users paste into input fields
- Clipboard monitoring: Track when users paste specific content patterns
- Data transfer: Move data between your app and other applications via clipboard
Permission and user intent
Clipboard operations work best when they happen because the user asked for them. A copy button, a paste action, or a shortcut tied to a clear interaction makes the browser behavior easy to understand. If the operation feels surprising, users are more likely to reject the permission prompt or ignore the feature altogether.
It is also worth handling the denied case gracefully. If the browser blocks access, show a message that explains what the user can do next instead of failing silently. A small bit of feedback makes the app feel more trustworthy and avoids the impression that the button is broken.
Text and rich content
Most examples deal with plain text, but clipboard workflows often become more useful when they understand structure. A code snippet might need formatting, a copied link might need a label, and a pasted value might need cleanup before you use it. Treat the clipboard as an input and output boundary, not just as a text field in disguise.
When you do more than copy a string, keep the user in control. Make it obvious what will be copied, and do not hide important transformations behind the button. Clear labels and predictable behavior matter more than cleverness here because the clipboard bridges your app and everything else on the desktop.
Fallback behavior
Older browsers or restricted contexts may still need a fallback path. In those cases, the important thing is not the exact API call. It is whether the user can still complete the task without confusion. If the modern Clipboard API fails, a legacy path can preserve the core experience, even if it is less elegant under the hood.
A good fallback should feel like the same feature, not a separate one. The user should still understand what was copied or pasted, and the app should still report success or failure clearly. That consistency makes the feature feel dependable across browsers and platforms.
Building copy and paste flows
Copy and paste features work best when the whole flow is obvious. If the button says what it copies, if the pasted content has a clear destination, and if the result is easy to confirm, the feature feels natural. The browser API handles the mechanics, but the product design still has to make the action feel deliberate.
Good clipboard UX also avoids surprise edits. If the app changes text after reading it from the clipboard, it should make that step clear so the user knows what happened. Small messages, consistent labels, and predictable results make these flows easier to trust.
Making the action visible
The clipboard is one of those features that disappears into the background when it works well. That is usually a good sign, but it also means the interface needs to do a little extra work to keep the action visible. A short confirmation, a label that names the copied value, or a small hint about where the content goes can make the interaction feel much clearer.
This matters even more when the copy action is tied to data that changes often, such as sharing links or temporary codes. A clear flow reduces the chance that users will paste the wrong thing or wonder whether the copy action succeeded. The goal is not to make the feature loud. The goal is to make it obvious.
Next steps
- Try adding a copy button to a code block on your own site
- Build a paste handler that sanitizes input before inserting it into a form
- Explore the Clipboard API spec for advanced features like custom MIME types
See Also
- Storage: localStorage, sessionStorage, and IndexedDB — persistent client-side storage APIs
- The Notifications API — displaying system notifications
- Fetch and XHR — making HTTP requests from the browser