There’s a new JavaScript API for asynchronous clipboard access with a spec that’s currently in the works. The de facto way to copy text to the clipboard on the web has been to use document.execCommand, but a major caveat is that the operations are synchronous and blocking. Async clipboard is a promise-based API that aims to fix that and also give us a simpler and unified API. On top of that, the API is designed to support data types other than just text/plain.

At this time, the new API is only available in Chrome 66+ and only copying/pasting of plain text is supported.

Note that the API only works when served over secured domains (https) or localhost and when the page is the browser's currently active tab.

Writing to the Clipboard

Writing to the clipboard is as simple as calling writeText with the text to write to the clipboard:

navigator.clipboard.writeText('Hello Alligator!') .then(() => { // Success! }) .catch(err => { console.log('Something went wrong', err); });

Reading from the Clipboard

To read from the clipboard, we use the readText method. To mitigate security risks, on top of needing to be in the active tab, an additional permission needs to be granted by the user. The browser will automatically ask for permission the first time readText is called.

navigator.clipboard.readText() .then(text => { // `text` contains the text read from the clipboard }) .catch(err => { // maybe user didn't grant access to read from clipboard console.log('Something went wrong', err); });

Simple Demo

Below is a really simplistic demonstration for writing to and reading from the clipboard. This demo will only work in supported browsers, but as an exercise to the reader, you could expand and add a fallback for non-supporting browsers.

Copy to clipboard Paste from clipboard

Let’s break it down with, first, the markup:

<div> <input type= "text" class= "to-copy" placeholder= "Type something..." aria-label= "Type something" > <button class= "write-btn" > Copy to clipboard </button> </div> <div> <h3 class= "clipboard-results" ></h3> <button class= "read-btn" > Paste from clipboard </button> </div>

And then finally, here’s the JavaScript code to make it all work:

const readBtn = document.querySelector('.read-btn'); const writeBtn = document.querySelector('.write-btn'); const resultsEl = document.querySelector('.clipboard-results'); const inputEl = document.querySelector('.to-copy'); readBtn.addEventListener('click', () => { navigator.clipboard.readText() .then(text => { resultsEl.innerText = text; }) .catch(err => { console.log('Something went wrong', err); }) }); writeBtn.addEventListener('click', () => { const inputValue = inputEl.value.trim(); if (inputValue) { navigator.clipboard.writeText(inputValue) .then(() => { inputEl.value = ''; if (writeBtn.innerText !== 'Copied!') { const originalText = writeBtn.innerText; writeBtn.innerText = 'Copied!'; setTimeout(() => { writeBtn.innerText = originalText; }, 1500); } }) .catch(err => { console.log('Something went wrong', err); }) } });

The code is really straightforward. The only place where we add a bit more logic is with the copy button, where we first make sure that there’s something to copy, and then also change the button’s text for a moment as well as reset the input’s value when the copy operation is successful.

The Future

The API also defines more generic write and a read methods for a way to copy or paste content other than just plain text, like with images. For example:

navigator.clipboard.read().then(({ items }) => { items.forEach(item => { console.log(item.type); // do something with the data item }); });

This is not supported in any browser at the time of this writing

Detecting Support

In order to degrade gracefully or to revert to using execCommand for non-supporting browsers, you can simply check for the presence of the clipboard object in the global navigator object:

if (navigator.clipboard) { // yep, turn the feature on. } else { // nope 😢. Use execCommand or leave the feature off }

Further Reading

If you want another great breakdown of the new API, have a look at this article by Jason Miller (@_developit).