The Clipboard API crashcourse

The hotkey everyone knows

Not everyone prefers to order their computer around with hotkeys. There’s a large group of users and programmers alike who rely on graphical menus and buttons. However, if there’s one hotkey combination everyone knows, it’s Cmd+C, Cmd+V. And yes, of course Ctrl+C, Ctrl+V on Windows. But what actually happens when you copy?

The Clipboard is a data buffer

Every major operating system comes with a “Clipboard”. It’s a data buffer for short-term storage, sometimes referred to as the “paste-buffer”. The new copy or cut command replaces the previous value in the buffer. Nearly all programs have access to this buffer. Otherwise we’d be annoyed when nothing happens when we hit Cmd+V.

Clipboard visualization as a data buffer

Automatically copying complex text can be good UX

Often on sites we need to present a user with a special code or text. This text is usually long and downright looks like gibberish. The user will likely copy it themself. However, what if they miss a character at the beginning or end?

What if they grab too much whitespace and you’re not trimming? What if they are just confused of what to copy? There’s too much room for error. A nice solution is to automatically copy this text to their clipboard. But where there’s convenience, there’s concern.

Example of copy and paste button for a value. Copying a TXT record on the Firebase Hosting Dashboard

The Clipboard has security concerns

There’s no concern when the user is in full control of the Clipboard. However, what happens when an app or program has programatic access to the Clipboard? I bet you can start to imagine some bad scenarios. What if an app secretly polls your Clipboard and sends the results to a server? That’s potentially sensitive information at risk like passwords, bank accounts, or any long security token you never wanted to type yourself. How do we combat this?

Security makes copy and paste weird on the web

Copy and paste has a strange history on the web. There’s no real standard. Most browsers have settled on document.execCommand('copy') and document.execCommand('paste'). While that seems easy enough, there’s some (justified) weirdness.

const result = document.executeCommand('copy');

If this is your first brush with document.execCommand('copy'), you might be wondering “Where is the text parameter?”. You might have got all excited, tried running document.execCommand('paste'), and saw it do absolutely nothing.

Without some paperwork you can’t just specify what text you want to add to the Clipboard. And you certainly can’t just access whatever’s on the clipboard at the time. There’s some rules you need to follow.

The rules of Clipboard access with executeCommand(‘copy’)

If you want to copy text with document.execCommand('copy'):

  1. The content must live in the DOM.
  2. The user needs to trigger the event (Firefox)
  3. The copied text is the user-selected text

Chrome doesn’t require a user-triggered event, but it is required in Firefox.

const button = document.querySelector('#btnCopy');
button.addEventListener('click', event => {
  console.log(document.execCommand('copy'));
});

What if you want to copy text that’s not in the DOM? You can inject it.

const button = document.querySelector('#btnCopy');
button.addEventListener('click', event => {
  const text = 'Hey look, this text is now in the DOM!';
  const input = document.createElement('input');
  document.body.appendChild(input);
  input.value = text;
  input.focus();
  input.select();
  console.log(document.execCommand('copy'));
});

If your text isn’t in the DOM, then make it in the DOM. The snippet above creates a input element, adds it to the DOM, inserts the text into the element, focuses it, selects it, and then finally copies it.

What happens if you don’t add the child to the DOM like indocument.body.appendChild(input)? The text isn’t copied. It can’t just be a detached DOM node, it needs to be in the DOM.

If you want to read directly from the clipboard with document.execCommand('paste'), you can’t. It doesn’t work on Chrome, Firefox, Edge, or IE. To get data off the Clipboard you must listen to the paste event. (Which isn’t supported in IE. The IE clipboard story is also strange. It’s a property on the window and requires permission, but let’s not get into that).

document.addEventListener('paste', event => {
  const text = event.clipboardData.getData('text/plain');
  console.log(text);
});

A user’s paste action triggers a ClipboardEvent. On the event you have access to the clipboardData property, which is a DataTransfer object. Calling getData() with the proper format returns the copied text. Okay, I’m sorry. That was a lot of spec links.

This is great because you don’t have ungranted access to the user’s Clipboard. A user must issue the paste. However, there’s a potential problem. The call to getData() is synchronous.

This poses a problem for you, the developer. What if they paste a massive base64-encoded image? Or some other insanely large amount of data? What if you do intense processing on this pasted data? This could block the main thread of the page, effectively freezing the page for your user.

Fortunately there’s an up and coming solution. It’s an entirely new way to read and write data from the Clipboard.

The Async Clipboard API

This new API has several improvements over our old friend document.execCommand('copy').

  • Individual commands for reading and writing from the Clipboard.
  • Async promise-based access to the Clipboard.
  • Permission based. The user must grant permission.
  • Doesn’t require a user event to trigger.

Note: This is Chrome only so far. This API is subject to change.

This new API is available on the Navigator object: navigator.clipboard

navigator.clipboard.writeText('whatever you need to copy').then(() => {
  console.log('Text copied');  
});

The user is prompted to grant permission when you issue this command for the first time. Since this API is asynchronous the main thread is clear while we wait for the user’s permission. If the API were synchronous, we’d be screwed.

To read from the Clipboard use readText():

navigator.clipboard.readText().then(text => {
  console.log(text);     
});

Just like before, if it’s the first time then the user must grant permission before you can access the Clipboard. Remember, the Clipboard value is hard to trust and the data can be sensitive. It’s still a good idea to ask the user to paste in a value where necessary.

document.addEventListener('paste', async event => {
  const text = await navigator.clipboard.readText();
  console.log(text);
});

This example is a big improvement over the synchronous getData() transfer method. The user must grant permission and you have async access. You’re better protected against a large amount of text and it’s easier to do more intensive processing on the pasted text.

Only the active tab has access to the clipboard

Users keep tabs open forever. Having unfettered access to the keyboard is still a bad idea. You could open example.com in a tab and leave it there for a week. Don’t act like you haven’t had a tab open for a week. During that time you’ll have all sorts of data go in and out of your Clipboard. What if example.com polled the navigator.clipboard.readText() method? That would be mighty dangerous, but there’s a safeguard.

Your site no longer has access to the Clipboard when the user switches to another tab. This is a great precaution, but it comes with an annoying debugging problem. When you open up the DevTools in Chrome, the DevTools itself becomes the active tab. The readText() or writeText() promise will reject and you’ll be annoyed and be all like “This is why I don’t use brand-new APIs”. The trick is to defer the call until you can click back into the tab.

setTimeout(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
}, 4000);

This isn’t much better, but it’ll work. This quirk isn’t the only drawback of the Async Clipboard API.

The Async Clipboard API doesn’t provide the user-selected text

Using document.execCommand('copy') will automatically copy the user’s selected text. If you need that functionality, you’ll have to get the selected text yourself and pass it in to writeText(). That will require a combination of the Selection API, the Range API, and some DOM traversal. You’ll have to deal with combining text nodes across a range of DOM elements, and that’s not fun.

Learn more about the Async Clipboard API

Jason Miller

Jason Miller, who is a legend, wrote an amazing article on the Async Clipboard API. It’s basically the standard resource at the moment.

Copy and paste responsibly

Clipboard access is a great tool for user experience, but it has its thorns. Some users carry sensitive data and some users bring malicious data. Make sure you handle user’s data responsibly and prepare yourself for those nasty paste events.