clipboard, clipboard-polyfill, copy-to-clipboard, react-copy-to-clipboard, and vue-clipboard2 are JavaScript libraries that simplify copying text to the system clipboard across different browsers and environments. They abstract away the complexities of the native document.execCommand('copy') API (now deprecated) and the modern asynchronous navigator.clipboard.writeText() API, handling fallbacks, permissions, and browser inconsistencies. These packages differ significantly in scope: some are framework-agnostic utilities (clipboard, clipboard-polyfill, copy-to-clipboard), while others provide React or Vue-specific bindings (react-copy-to-clipboard, vue-clipboard2).
Copying text to the clipboard seems simple, but browser inconsistencies, security restrictions, and the deprecation of execCommand make it surprisingly tricky. The five packages under review tackle this problem differently — from DOM-centric helpers to modern promise-based utilities and framework wrappers. Let’s cut through the noise and see which tool fits your architecture.
Before diving in, note that vue-clipboard2 is officially deprecated. Its GitHub repository and npm page state: "This package is no longer maintained. Please use @soerenmartius/vue3-clipboard instead." Do not use it in new projects. We’ll still cover it briefly for legacy context, but treat it as a cautionary tale.
clipboard: Declarative DOM Bindingclipboard attaches copy behavior directly to HTML elements using data attributes. You never call a function — just add attributes like data-clipboard-text to a button, and the library handles the rest on click.
<!-- HTML setup -->
<button id="btn" data-clipboard-text="Hello World">Copy</button>
// JS initialization
import ClipboardJS from 'clipboard';
new ClipboardJS('#btn');
It listens for clicks, extracts text from attributes, and attempts to copy. Works only in response to user gestures (required by browser security).
clipboard-polyfill: Standards-Based Promise APIclipboard-polyfill provides a clean, modern interface aligned with the official navigator.clipboard API. It returns promises and handles fallbacks internally.
import { writeText } from 'clipboard-polyfill';
async function copyHandler() {
try {
await writeText('Hello World');
console.log('Copied!');
} catch (err) {
console.error('Failed to copy', err);
}
}
It automatically uses navigator.clipboard.writeText() when available and falls back to legacy methods (with proper sandboxing) in older browsers. Also works in non-DOM environments like Web Workers.
copy-to-clipboard: Minimalist Functioncopy-to-clipboard exports a single synchronous function that tries to copy text and returns a boolean success status.
import copy from 'copy-to-clipboard';
function handleClick() {
const success = copy('Hello World');
if (success) {
console.log('Copied!');
}
}
No promises, no callbacks — just fire-and-forget. Internally, it uses a temporary textarea element to perform the copy operation.
react-copy-to-clipboard: React Component WrapperThis package wraps copy-to-clipboard in a React component. You render it with text and onCopy props, and it clones its child with extra props.
import { CopyToClipboard } from 'react-copy-to-clipboard';
function App() {
return (
<CopyToClipboard text="Hello World" onCopy={() => console.log('Copied!')}>
<button>Copy</button>
</CopyToClipboard>
);
}
Under the hood, it calls copy-to-clipboard when the child element is clicked.
vue-clipboard2: Deprecated Vue DirectiveHistorically, this provided a Vue directive for copying:
// ❌ DO NOT USE — deprecated
import VueClipboard from 'vue-clipboard2';
Vue.use(VueClipboard);
<template>
<button v-clipboard:copy="'Hello World'">Copy</button>
</template>
But again — this package is unmaintained and should be avoided.
All these libraries must contend with two hard constraints:
navigator.clipboard outside of secure contexts.Here’s how they handle it:
clipboard: Only works on click events. Fails silently in unsupported contexts.clipboard-polyfill: Uses the standard permission API when possible. Throws clear errors when blocked, and includes a shouldShowPermissionPrompt() helper.copy-to-clipboard: Tries to copy immediately. Returns false if blocked, but gives no details why.react-copy-to-clipboard: Inherits copy-to-clipboard’s behavior — no error context.vue-clipboard2: Same as clipboard — gesture-bound with minimal error feedback.Your choice depends heavily on whether you prefer programmatic control (calling functions from JS) or declarative binding (attaching behavior via HTML/JSX).
clipboard-polyfill if you want modern promise handling and detailed error reporting.copy-to-clipboard if you just need a fire-and-forget boolean result.// clipboard-polyfill: async/await with error details
try {
await writeText(text);
} catch (err) {
if (err.name === 'NotAllowedError') {
showPermissionGuide();
}
}
// copy-to-clipboard: simple boolean
if (!copy(text)) {
alert('Copy failed — try manually');
}
clipboard for vanilla JS/jQuery apps with static buttons.react-copy-to-clipboard if you’re in React and like component composition.<!-- clipboard: pure HTML -->
<button data-clipboard-text="...">Copy</button>
{/* react-copy-to-clipboard: JSX */}
<CopyToClipboard text="..."><button>Copy</button></CopyToClipboard>
react-copy-to-clipboard feels natural but adds an extra dependency layer. Many teams skip it and call clipboard-polyfill directly inside event handlers for more control.// Common React pattern without wrapper
function CopyButton({ text }) {
const handleCopy = async () => {
try {
await writeText(text);
// update UI state, etc.
} catch (err) { /* handle */ }
};
return <button onClick={handleCopy}>Copy</button>;
}
vue-clipboard2 is dead, the best path is using clipboard-polyfill directly in methods:// Vue 3 Composition API example
import { writeText } from 'clipboard-polyfill';
export default {
methods: {
async copyText(text) {
try {
await writeText(text);
// handle success
} catch (err) {
// handle error
}
}
}
};
clipboard-polyfill is the safest bet for new projects. It’s actively maintained, follows web standards, and works everywhere from SPAs to Web Workers.| Package | API Style | Async Support | Error Details | Framework-Specific | Maintenance Status |
|---|---|---|---|---|---|
clipboard | Declarative DOM | ❌ | ❌ | None | ✅ Active |
clipboard-polyfill | Programmatic | ✅ Promise | ✅ | None | ✅ Active |
copy-to-clipboard | Programmatic | ❌ | ❌ (boolean) | None | ✅ Active |
react-copy-to-clipboard | React Component | ❌ | ❌ | React | ✅ Active |
vue-clipboard2 | Vue Directive | ❌ | ❌ | Vue 2 | ❌ Deprecated |
clipboard-polyfill. It’s the most future-proof, gives you detailed error handling, and aligns with web standards. Call it directly from your framework’s event handlers.copy-to-clipboard is fine.clipboard works well if you’re already using data attributes extensively.react-copy-to-clipboard unless you strongly prefer its component API — most teams gain more flexibility by using clipboard-polyfill directly.vue-clipboard2 entirely. Use clipboard-polyfill in methods or composables.Remember: no library can bypass browser security. Always design your UI to handle copy failures gracefully — show a fallback tooltip, highlight the text for manual selection, or guide users through permission prompts when needed.
Choose clipboard if you need a DOM-focused utility that binds copy behavior directly to HTML elements via data attributes, and your app uses buttons or other interactive elements where declarative markup is preferred. It’s ideal for simple static sites or jQuery-style apps but requires manual DOM setup and doesn’t support programmatic copying outside of user-triggered events.
Choose clipboard-polyfill if you require a lightweight, modern, framework-agnostic solution that prioritizes standards compliance and works reliably in both browser and non-browser environments (like Web Workers). It supports both promise-based and callback APIs, handles permission prompts gracefully, and actively maintains compatibility with the latest web standards.
Choose copy-to-clipboard if you want the simplest possible function-based API for programmatic copying without any DOM coupling. It’s a good fit for utility-heavy applications where you trigger copies from JavaScript logic rather than HTML attributes, and you prefer minimal abstraction over feature richness.
Choose react-copy-to-clipboard only if you’re working in a React codebase and prefer a component-based API that integrates naturally with JSX. Note that it wraps copy-to-clipboard under the hood, so it inherits its simplicity but adds React-specific ergonomics like render props or children-as-function patterns.
Avoid vue-clipboard2 in new projects — it is officially deprecated as of 2021 and unmaintained. The author recommends migrating to @soerenmartius/vue3-clipboard for Vue 3 or using a framework-agnostic alternative like clipboard-polyfill. Do not use it for production applications due to lack of updates and security patches.
Modern copy to clipboard. No Flash. Just 3kb gzipped.
Copying text to the clipboard shouldn't be hard. It shouldn't require dozens of steps to configure or hundreds of KBs to load. But most of all, it shouldn't depend on Flash or any bloated framework.
That's why clipboard.js exists.
You can get it on npm.
npm install clipboard --save
Or if you're not into package management, just download a ZIP file.
First, include the script located on the dist folder or load it from a third-party CDN provider.
<script src="dist/clipboard.min.js"></script>
Now, you need to instantiate it by passing a DOM selector, HTML element, or list of HTML elements.
new ClipboardJS('.btn');
Internally, we need to fetch all elements that matches with your selector and attach event listeners for each one. But guess what? If you have hundreds of matches, this operation can consume a lot of memory.
For this reason we use event delegation which replaces multiple event listeners with just a single listener. After all, #perfmatters.
We're living a declarative renaissance, that's why we decided to take advantage of HTML5 data attributes for better usability.
A pretty common use case is to copy content from another element. You can do that by adding a data-clipboard-target attribute in your trigger element.
The value you include on this attribute needs to match another's element selector.
<!-- Target -->
<input id="foo" value="https://github.com/zenorocha/clipboard.js.git" />
<!-- Trigger -->
<button class="btn" data-clipboard-target="#foo">
<img src="https://raw.githubusercontent.com/zenorocha/clipboard.js/HEAD/assets/clippy.svg" alt="Copy to clipboard" />
</button>
Additionally, you can define a data-clipboard-action attribute to specify if you want to either copy or cut content.
If you omit this attribute, copy will be used by default.
<!-- Target -->
<textarea id="bar">Mussum ipsum cacilds...</textarea>
<!-- Trigger -->
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">
Cut to clipboard
</button>
As you may expect, the cut action only works on <input> or <textarea> elements.
Truth is, you don't even need another element to copy its content from. You can just include a data-clipboard-text attribute in your trigger element.
<!-- Trigger -->
<button
class="btn"
data-clipboard-text="Just because you can doesn't mean you should — clipboard.js"
>
Copy to clipboard
</button>
There are cases where you'd like to show some user feedback or capture what has been selected after a copy/cut operation.
That's why we fire custom events such as success and error for you to listen and implement your custom logic.
var clipboard = new ClipboardJS('.btn');
clipboard.on('success', function (e) {
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
e.clearSelection();
});
clipboard.on('error', function (e) {
console.error('Action:', e.action);
console.error('Trigger:', e.trigger);
});
For a live demonstration, go to this site and open your console.
Each application has different design needs, that's why clipboard.js does not include any CSS or built-in tooltip solution.
The tooltips you see on the demo site were built using GitHub's Primer. You may want to check that out if you're looking for a similar look and feel.
If you don't want to modify your HTML, there's a pretty handy imperative API for you to use. All you need to do is declare a function, do your thing, and return a value.
For instance, if you want to dynamically set a target, you'll need to return a Node.
new ClipboardJS('.btn', {
target: function (trigger) {
return trigger.nextElementSibling;
},
});
If you want to dynamically set a text, you'll return a String.
new ClipboardJS('.btn', {
text: function (trigger) {
return trigger.getAttribute('aria-label');
},
});
For use in Bootstrap Modals or with any other library that changes the focus you'll want to set the focused element as the container value.
new ClipboardJS('.btn', {
container: document.getElementById('modal'),
});
Also, if you are working with single page apps, you may want to manage the lifecycle of the DOM more precisely. Here's how you clean up the events and objects that we create.
var clipboard = new ClipboardJS('.btn');
clipboard.destroy();
This library relies on both Selection and execCommand APIs. The first one is supported by all browsers while the second one is supported in the following browsers.
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
|---|---|---|---|---|---|
| 42+ ✔ | 12+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | 10+ ✔ |
The good news is that clipboard.js gracefully degrades if you need to support older browsers. All you have to do is show a tooltip saying Copied! when success event is called and Press Ctrl+C to copy when error event is called because the text is already selected.
You can also check if clipboard.js is supported or not by running ClipboardJS.isSupported(), that way you can hide copy/cut buttons from the UI.
A browser extension that adds a "copy to clipboard" button to every code block on GitHub, MDN, Gist, StackOverflow, StackExchange, npm, and even Medium.
Install for Chrome and Firefox.
MIT License © Zeno Rocha