copy-to-clipboard vs react-copy-to-clipboard vs clipboard-copy vs ngclipboard vs vue-clipboard2
Copying Text to Clipboard in Web Applications
copy-to-clipboardreact-copy-to-clipboardclipboard-copyngclipboardvue-clipboard2Similar Packages:

Copying Text to Clipboard in Web Applications

clipboard-copy, copy-to-clipboard, ngclipboard, react-copy-to-clipboard, and vue-clipboard2 are npm packages that help developers implement copy-to-clipboard functionality in web applications. They abstract away browser inconsistencies and security restrictions around the Clipboard API, offering simple interfaces to copy text programmatically or via user interaction. While some are framework-agnostic utilities (clipboard-copy, copy-to-clipboard), others are tightly integrated with specific frontend frameworks like Angular (ngclipboard), React (react-copy-to-clipboard), or Vue.js (vue-clipboard2).

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
copy-to-clipboard8,194,0191,39833.5 kB165 months agoMIT
react-copy-to-clipboard1,596,2292,36837.9 kB156 months agoMIT
clipboard-copy389,783632-76 years agoMIT
ngclipboard16,51238419.4 kB23-MIT
vue-clipboard201,752-375 years agoMIT

Copying Text to Clipboard: A Practical Comparison of Popular npm Packages

Copying text to the clipboard is a common UI requirement — think “copy invite link” buttons or “copy code snippet” actions. But browser security rules make this tricky: you can’t just write to the clipboard whenever you want. You need either a user gesture (like a click) or permission via the modern Async Clipboard API. The packages clipboard-copy, copy-to-clipboard, ngclipboard, react-copy-to-clipboard, and vue-clipboard2 all aim to simplify this, but they do it in very different ways depending on your stack and needs.

🧰 Core Approach: Utility Function vs Framework Integration

clipboard-copy – Element-First, Zero Dependencies

This package takes a DOM element and copies its text content (or a custom value) using the best available method. It’s designed to be called inside event handlers.

// clipboard-copy
import copy from 'clipboard-copy';

document.getElementById('copy-btn').addEventListener('click', async () => {
  await copy('Hello, clipboard!');
  console.log('Copied!');
});

It also supports passing a DOM element whose .textContent will be copied:

const el = document.querySelector('#secret-code');
await copy(el); // Copies el.textContent

Under the hood, it tries the modern navigator.clipboard.writeText() first, falling back to document.execCommand('copy') in older browsers.

copy-to-clipboard – Promise-Based, No DOM Required

This is a pure function that takes a string and returns a promise. No element reference needed.

// copy-to-clipboard
import copy from 'copy-to-clipboard';

async function handleCopy() {
  try {
    await copy('https://example.com/share?token=abc123');
    alert('Link copied!');
  } catch (err) {
    console.error('Failed to copy', err);
  }
}

It uses the same fallback strategy as clipboard-copy but exposes a cleaner async interface. Great when your copy logic lives in a service or utility file, not tied to a specific button.

ngclipboard – Angular Directive (Deprecated Pattern)

This package provides an Angular directive that binds to a string value and triggers copy on click.

<!-- ngclipboard (Angular) -->
<button [ngClipboard]="'Text to copy'" (cbOnSuccess)="onCopied()">
  Copy
</button>

However, the repository hasn’t been updated for Angular Ivy or recent versions. It relies on Renderer2 and manual event binding, and there’s no official support for standalone components or signals. Given its inactive status, avoid in new Angular projects.

react-copy-to-clipboard – React Component with State Feedback

This wraps copy logic in a React component that manages success state for you.

// react-copy-to-clipboard
import { CopyToClipboard } from 'react-copy-to-clipboard';

function ShareButton() {
  const [copied, setCopied] = useState(false);

  return (
    <CopyToClipboard text="invite@example.com" onCopy={() => setCopied(true)}>
      <button>{copied ? 'Copied!' : 'Copy Email'}</button>
    </CopyToClipboard>
  );
}

It uses copy-to-clipboard under the hood but adds React-specific ergonomics. Note: it doesn’t use hooks internally (as of latest release), so it’s class-component friendly but slightly heavier than calling copy-to-clipboard directly in a useCallback.

vue-clipboard2 – Vue 2 Plugin with Directive

For Vue 2, this registers a global directive and a $copyText method.

// vue-clipboard2 (Vue 2)
// In main.js
import VueClipboard from 'vue-clipboard2';
Vue.use(VueClipboard);

Then in a component:

<template>
  <button v-clipboard:copy="text" v-clipboard:success="onCopy">Copy</button>
</template>

<script>
export default {
  data() {
    return { text: 'Hello from Vue!' };
  },
  methods: {
    onCopy() {
      this.$message('Copied!');
    }
  }
};
</script>

But this package does not work with Vue 3. The Vue 3 ecosystem has moved toward composable functions, and this plugin hasn’t adapted. For Vue 3, skip this and use copy-to-clipboard inside a @click handler.

⚠️ Maintenance and Compatibility Reality Check

  • ngclipboard: Last published in 2018. No support for Angular 9+. Repository archived or inactive. Do not use in new projects.
  • vue-clipboard2: Last update in 2020. Explicitly Vue 2 only. No Vue 3 compatibility. Avoid for Vue 3 apps.
  • react-copy-to-clipboard: Actively maintained as of 2023, supports React 16–18, but doesn’t leverage modern hooks patterns.
  • clipboard-copy and copy-to-clipboard: Both actively maintained, framework-agnostic, and support modern browsers with graceful fallbacks.

🔁 Fallback Strategy: How They Handle Older Browsers

All packages attempt to use navigator.clipboard.writeText() first (secure, async, requires HTTPS or localhost). If unavailable (e.g., Safari < 13.1, old Chrome), they fall back to the deprecated but widely supported document.execCommand('copy').

The fallback requires creating a temporary <textarea>, adding it to the DOM, selecting its content, and triggering execCommand. Both clipboard-copy and copy-to-clipboard handle this cleanly:

// Simplified fallback logic (used by both)
function fallbackCopyText(text) {
  const textarea = document.createElement('textarea');
  textarea.value = text;
  textarea.style.position = 'fixed';
  document.body.appendChild(textarea);
  textarea.select();
  try {
    return document.execCommand('copy');
  } finally {
    document.body.removeChild(textarea);
  }
}

Framework-specific packages inherit this behavior from their underlying utilities.

🛠️ When to Use What: Real Engineering Trade-offs

Use copy-to-clipboard if:

  • You’re in a framework-agnostic environment (Svelte, Solid, vanilla JS).
  • Your copy logic is decoupled from the DOM (e.g., triggered by a Redux action or service call).
  • You want clean async/await syntax and don’t need automatic UI feedback.

Use clipboard-copy if:

  • You already have a reference to a DOM element (e.g., in a web component or jQuery-style code).
  • You want the smallest possible footprint with no extra abstractions.
  • You’re building a design system or low-level utility library.

Use react-copy-to-clipboard if:

  • You’re in a React app and want built-in success state without writing useState boilerplate.
  • Your team prefers declarative components over imperative function calls.
  • You’re okay with a small wrapper around copy-to-clipboard.

Avoid ngclipboard and vue-clipboard2 in new projects because:

  • They’re tied to outdated framework versions.
  • Maintenance is uncertain.
  • Modern alternatives (native Clipboard API + lightweight utils) are more reliable.

💡 Pro Tip: In 2024, most apps can safely use the native navigator.clipboard.writeText() directly if you control your browser support matrix. Only reach for a package if you need IE11 or old Safari support — and even then, copy-to-clipboard is the safest bet.

✅ Final Recommendation

For new projects, default to copy-to-clipboard — it’s simple, well-maintained, and works everywhere. If you’re deep in React and love components, react-copy-to-clipboard is fine. But steer clear of ngclipboard and vue-clipboard2 unless you’re maintaining a legacy Angular or Vue 2 app with no upgrade path.

Remember: the clipboard is a user-facing feature. Always provide visual feedback (“Copied!”) and never copy without explicit user intent — browsers will block silent clipboard writes anyway.

How to Choose: copy-to-clipboard vs react-copy-to-clipboard vs clipboard-copy vs ngclipboard vs vue-clipboard2

  • copy-to-clipboard:

    Choose copy-to-clipboard if you prefer a promise-based, framework-agnostic function that copies text without requiring a DOM element reference. It’s well-suited for logic-heavy applications where clipboard operations are triggered from business logic rather than direct UI events, and you want clean async/await syntax.

  • react-copy-to-clipboard:

    Choose react-copy-to-clipboard if you’re building a React app and want a component that handles clipboard state (like success feedback) declaratively. It wraps the underlying copy logic in a render prop or child function pattern, making it easy to show UI changes after copying, though it adds a small layer of abstraction over simpler utility functions.

  • clipboard-copy:

    Choose clipboard-copy if you need a tiny, zero-dependency utility that works directly with DOM elements and supports both modern Clipboard API and legacy document.execCommand() fallbacks. It’s ideal for vanilla JavaScript projects or when you want minimal abstraction and full control over element selection and event handling.

  • ngclipboard:

    Choose ngclipboard only if you’re working on an Angular application and want a directive-based approach that integrates with Angular’s template syntax. However, note that the package appears unmaintained and lacks support for modern Angular versions — consider using Angular’s built-in renderer or a lightweight alternative instead.

  • vue-clipboard2:

    Choose vue-clipboard2 if you’re using Vue 2 and need a directive (v-clipboard) or programmatic API that fits naturally into Vue’s reactivity system. Be aware it is not compatible with Vue 3, and the project shows signs of abandonment — for new Vue 3 projects, use the native Clipboard API or a framework-agnostic utility.

README for copy-to-clipboard

Copy to clipboard CI

Simple module exposing an async copy function that uses the Async Clipboard API (navigator.clipboard) in secure contexts (HTTPS / localhost), with automatic fallback to document.execCommand('copy') for non-secure contexts or older browsers.

If you are building using Electron, use their API.

Example

import copy from 'copy-to-clipboard';

await copy('Text');

// Copy with options
await copy('Text', {
  debug: true,
  message: 'Press #{key} to copy',
});

// Copy as HTML (text/html + text/plain written simultaneously)
await copy('<b>Hello <i>world</i></b>', { format: 'text/html' });

// Custom plain-text fallback via onCopy
await copy('<b>Hello <i>world</i></b>', {
  format: 'text/html',
  onCopy: () => new ClipboardItem({
    'text/html': new Blob(['<b>Hello <i>world</i></b>'], { type: 'text/html' }),
    'text/plain': new Blob(['Hello world'], { type: 'text/plain' }),
  }),
});

API

copy(text: string, options?: object): Promise<boolean> — copies text to clipboard. Returns true on success, false if all paths failed (no additional keystrokes were required from the user).

v4 breaking change: copy() is now async and returns Promise<boolean>. Use await copy(...) to get the result.

Options

ValueDefaultNotes
options.debugfalseBoolean. Enable output to console.
options.message'Copy to clipboard: #{key}, Enter'String. Prompt message used when fallbackToPrompt is enabled. All occurrences of #{key} are replaced with ⌘+C on macOS or Ctrl+C otherwise.
options.formatString. MIME type to copy as. Use 'text/html' to copy rich text; 'text/plain' to strip inherited styles when pasting into rich-text editors. When set alongside 'text/html', both text/html and text/plain are written simultaneously via ClipboardItem.
options.onCopy(clipboardData: ClipboardItem | DataTransfer) => ClipboardItem | void. Called before the write. On the async path, receives the constructed ClipboardItem and may return a new one to replace it (useful for custom MIME types or a different plain-text fallback). On the execCommand fallback path, receives the DataTransfer object; return value is ignored.
options.fallbackToPromptfalseBoolean. If true, shows a window.prompt() as a last resort when both navigator.clipboard and execCommand fail. Off by default in v4.

Copy path selection

  1. navigator.clipboard.writeText(text) — used when the page is a secure context (HTTPS / localhost), navigator.clipboard is available, and neither options.format nor options.onCopy is set.
  2. navigator.clipboard.write([ClipboardItem]) — used in a secure context when options.format or options.onCopy is set. Builds a ClipboardItem with text/plain always present; adds the requested MIME type alongside it. onCopy may return a replacement ClipboardItem.
  3. execCommand('copy') fallback — used on non-HTTPS pages, when navigator.clipboard is unavailable, or when the async write throws. Uses a hidden <span> element. preventDefault is only called when options.format is set.
  4. window.prompt() fallback — opt-in via options.fallbackToPrompt: true.

Recipes

HTML copy with stripped plain-text fallback

By default, copy(html, { format: 'text/html' }) puts the raw HTML string in the text/plain slot of the ClipboardItem. If you want apps that only accept plain text to receive readable content instead of markup, use onCopy to supply a stripped version:

function stripHtml(html) {
  const div = document.createElement('div');
  div.innerHTML = html;
  return div.textContent || div.innerText || '';
}

const html = '<b>Hello <i>world</i></b>';

await copy(html, {
  format: 'text/html',
  onCopy: () => new ClipboardItem({
    'text/html': new Blob([html], { type: 'text/html' }),
    'text/plain': new Blob([stripHtml(html)], { type: 'text/plain' }),
  }),
});
// text/html  → '<b>Hello <i>world</i></b>'
// text/plain → 'Hello world'

Custom MIME type

await copy('col1,col2\nval1,val2', {
  format: 'text/csv',
});
// text/csv   → 'col1,col2\nval1,val2'
// text/plain → 'col1,col2\nval1,val2'  (always included as fallback)

Browser support

BrowserMinimumNotes
Chrome76+Full ClipboardItem + write() support
Firefox127+Full ClipboardItem + write() landed in Firefox 127 (mid-2024)
Safari13.1+writeText() and write() available
Edge79+Chromium-based; same as Chrome

execCommand fallback retains support for non-HTTPS contexts and any browser that reaches the catch path.

Note: The async clipboard write must occur within a user gesture (click, keydown, etc.). This library is designed to be called from event handlers, so this constraint is normally satisfied automatically.

Installation

npm i copy-to-clipboard

Available as CommonJS, ES module, and IIFE (for <script> tags):

// ESM
import copy from 'copy-to-clipboard';

// CommonJS
const copy = require('copy-to-clipboard');
<!-- IIFE via CDN — exposes window.copyToClipboard -->
<script src="https://unpkg.com/copy-to-clipboard/dist/index.global.js"></script>

TypeScript

Built-in declarations are included for both CommonJS and ESM consumers.

import copy from 'copy-to-clipboard';
import type { Options } from 'copy-to-clipboard';

const result: boolean = await copy('text');

UI components based on this package

See also

Running Tests

End-to-end tests are powered by Nightwatch using native browser drivers.

npm i
npm test               # Chrome (default)
npm run test:firefox
npm run test:safari
npm run test:edge
npm run test:all       # all local browsers

Safari prerequisite: enable "Allow Remote Automation" in Safari's Develop menu. See Testing with WebDriver in Safari.

CI

Chrome, Firefox, and Edge tests run automatically on every push to master and on pull requests via GitHub Actions (Ubuntu runner, headless).

Cross-browser tests (Chrome, Firefox, Safari, Edge) run on LambdaTest automatically on every version tag (v*) and can be triggered manually from the Actions tab. Requires LT_USERNAME and LT_ACCESS_KEY repository secrets.

npm run test:lt:chrome
npm run test:lt:firefox
npm run test:lt:safari
npm run test:lt:edge
npm run test:lt:all

Breaking changes in v4

  1. copy() is now async — returns Promise<boolean> instead of boolean. Wrap call sites with await.
  2. IE11 support droppedwindow.clipboardData path removed.
  3. window.prompt() fallback is opt-in — set options.fallbackToPrompt: true to enable.
  4. Build output moved to dist/ — direct require('copy-to-clipboard/index.js') paths will break; use the package name only.