copy-to-clipboard
v4.0.0
Published
Copy stuff into clipboard using JS with fallbacks
Downloads
41,409,386
Readme
Copy to clipboard 
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.
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 returnsPromise<boolean>. Useawait copy(...)to get the result.
Options
| Value | Default | Notes |
|-------|---------|-------|
| options.debug | false | Boolean. 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.format | — | String. 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.fallbackToPrompt | false | Boolean. 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
navigator.clipboard.writeText(text)— used when the page is a secure context (HTTPS / localhost),navigator.clipboardis available, and neitheroptions.formatnoroptions.onCopyis set.navigator.clipboard.write([ClipboardItem])— used in a secure context whenoptions.formatoroptions.onCopyis set. Builds aClipboardItemwithtext/plainalways present; adds the requested MIME type alongside it.onCopymay return a replacementClipboardItem.execCommand('copy')fallback — used on non-HTTPS pages, whennavigator.clipboardis unavailable, or when the async write throws. Uses a hidden<span>element.preventDefaultis only called whenoptions.formatis set.window.prompt()fallback — opt-in viaoptions.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
| Browser | Minimum | Notes |
|---------|---------|-------|
| Chrome | 76+ | Full ClipboardItem + write() support |
| Firefox | 127+ | Full ClipboardItem + write() landed in Firefox 127 (mid-2024) |
| Safari | 13.1+ | writeText() and write() available |
| Edge | 79+ | 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-clipboardAvailable 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 browsersSafari 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:allBreaking changes in v4
copy()is now async — returnsPromise<boolean>instead ofboolean. Wrap call sites withawait.- IE11 support dropped —
window.clipboardDatapath removed. window.prompt()fallback is opt-in — setoptions.fallbackToPrompt: trueto enable.- Build output moved to
dist/— directrequire('copy-to-clipboard/index.js')paths will break; use the package name only.
