Using the JavaScript Clipboard API
Tags: javascript, webdev, api
Published: 2022-10-04 23:01:04

by [Craig Buckler](https://blog.openreplay.com/authors/craig-buckler)


Until 2020, implementing cross-browser cut, copy, and paste options in web apps required convoluted synchronous [`document.execCommand()`](https://developer.mozilla.org/docs/Web/API/Document/execCommand) code which rarely worked as expected. This is now deprecated in favor of a new [asynchronous Clipboard API](https://developer.mozilla.org/docs/Web/API/Clipboard_API). Support is generally good, although not all browsers support the full specification.


## Why do Apps Need Programmatic Clipboard Access?

Those with a reasonable level of IT literacy often use keyboard shortcuts to cut, copy, and paste:

* `Ctrl` / `Cmd` + `C` to copy
* `Ctrl` / `Cmd` + `X` to cut
* `Ctrl` / `Cmd` + `V` to paste
* `Ctrl` / `Cmd` + `Shift` + `V` to paste without formatting
* perhaps similar options to access clipboard history managers
* long-press on smartphones and tablets.

This raises several issues:

* These shortcuts are not necessarily apparent to those with limited computing experience.

* Selecting the correct text can be difficult -- we've all accidentally highlighted unnecessary whitespace when copying passwords and access codes.

* How do you copy non-textual content such as images or audio data? How do you copy SVG images generated using text code?!

The Clipboard API allows you to:

* offer UI controls such as copy and paste buttons
* handle text and binary formats
* modify content when the user activates clipboard actions.


## Clipboard Crisis

Browser vendors had to consider security concerns when implementing the API. Your clipboard could contain personal or secure data such as confidential documents or passwords. Therefore, the Clipboard API is only available when the web page:

* uses HTTPS (or a `localhost` address during development)
* is in an active browser tab and not in the background, and
* the functionality is triggered by user interaction, such as a click.

If your page is running inside an `<iframe>`, the parent page must set explicit clipboard permissions:

```html
<iframe
  src="https://site.com/child.html"
  allow="clipboard-read; clipboard-write"
></iframe>
```

Finally, the user may see a permission prompt in some browsers -- typically the first time they use paste functionality:

![clipboard permission prompt](https://blog.openreplay.com/images/using-the-javascript-clipboard-api/images/clipboard-approve.png)

The [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)  allows you to check the `clipboard-read` and `clipboard-write` states and force a prompt. Be aware these are non-standard and only available in Chromium-based browsers (Chrome, Edge, Opera, Brave, and Vivaldi).


## Detect Clipboard API Support

You can detect Clipboard API support using `navigator.clipboard`:

```javascript
if (navigator.clipboard) {
  console.log('Clipboard API available');
}
```

This confirms that *some* API features are available. It will be necessary to make further checks for specific methods when attempting to manipulate the clipboard, e.g.

```javascript
if (navigator.clipboard.writeText) {
  console.log('Can copy text to clipboard');
}
```

## Write Text to the Clipboard

To write any text to the clipboard, use the Promise-based [`writeText()`](https://developer.mozilla.org/docs/Web/API/Clipboard/writeText) method:

```javascript
document.getElementById('copybutton').addEventListener('click', async () => {

  try {
    await navigator.clipboard.writeText('added to clipboard');
  }
  catch (err) {
    console.error('Could not write to clipboard', err);
  }

});
```

This code writes text to the clipboard when an element with the ID `copybutton` is clicked. The browser checks for recent user interaction, so it's generally best to run the `writeText()` method within an event handler.

You can write any string to the clipboard but, if necessary, you can get whatever text the user has selected on the page and manipulate it as necessary:

```javascript
const selectedText = window.getSelection().toString().trim();

if (selectedText) {
  await navigator.clipboard.writeText(
    selectedText + '\nCopied from site.com';
  );
}
```

The `writeText()` method has full support in most modern browsers.


## Read Text from the Clipboard

To read text content from the clipboard, use the Promise-based [`readText()`](https://developer.mozilla.org/docs/Web/API/Clipboard/readText) method:

```javascript
document.getElementById('pastebutton').addEventListener('click', async () => {

  let text = '';

  try {
    text = await navigator.clipboard.readText();
  }
  catch (err) {
    console.error('Could not read from clipboard', err);
  }

  if (text) {
    // use text accordingly
  }

}
```

This code reads text from the clipboard when an element with the ID `pastebutton` is clicked. The browser checks for recent user interaction, so it's generally best to execute `readText()` within an event handler.

The `readText()` method has full support in most modern browsers.


<h2>Open Source Session Replay</h2> 

_<a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a> is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![OpenReplay](https://blog.openreplay.com/banner-blog_1oYPGT.png)

_Start enjoying your debugging experience - <a href="https://github.com/openreplay/openreplay" >start using OpenReplay for free</a>._






## Write Any Data to the Clipboard

The [`write()`](https://developer.mozilla.org/docs/Web/API/Clipboard/write) method supports all data types, including binary and text. However, browser support is more limited, and the code is not as simple.

An array of [`ClipboardItem`](https://developer.mozilla.org/docs/Web/API/ClipboardItem) objects is passed to the `write()` method. These objects are constructed by passing an object with the MIME type as the key and raw data [Blob](https://developer.mozilla.org/docs/Web/API/Blob) as the value. The following code creates a `ClipboardItem` object from a text string:

```javascript
const
  type = 'text/plain',
  blob1 = new Blob(['text to write to clipboard'], { type }),
  item1 = new ClipboardItem({ [type]: blob });
```

The following example creates a `ClipboardItem` object from an image loaded using the [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API):


```javascript
const
  img   = await fetch('image.png'),
  blob2 = await img.blob(),
  item2 = new ClipboardItem({ [blob.type]: blob };
```

The following example creates a `ClipboardItem` object from an image rendered in a canvas element with the id `mycanvas`:

```javascript
const
  canv  = document.getElementById(`mycanvas`),
  blob3 = await canv.toBlob(),
  item3 = new ClipboardItem({ [blob.type]: blob };
```

You can now write the resulting `item` object(s) to a single clipboard entry:

```javascript
try {
  await navigator.clipboard.write([ item1, item2, item3 ]);
}
catch (err) {
  console.error('Could not write to clipboard', err);
}
```

Remember the browser checks for a recent user interaction, so this code should normally be run within an event handler.


## Read Any Data from the Clipboard

The [`read()`](https://developer.mozilla.org/docs/Web/API/Clipboard/read) method supports all data types, including binary and text. Again, browser support is more limited. It returns an array of [`ClipboardItem`](https://developer.mozilla.org/docs/Web/API/ClipboardItem) objects which you can process as required:

```javascript
try {

  const clipboard = await navigator.clipboard.read();

  for (item of clipboard) {

    for (type of item.types) {

      // get blob data for item
      const blob = await item.getType(type);

      let newElement;

      // text item
      if (type === 'text/plain') {
        newElement = document.createElement('p');
        newElement.textContent = await blob.text();
      }

      // image item
      if (type.startsWith('image')) {
        newElement = new Image(),
        newElement.src = URL.createObjectURL(blob);
      }

      // append text or image to document
      if (newElement) {
        document.body.appendChild(newElement);
      }

    }

  }

}
catch (err) {
  console.error('Could not read from clipboard', err);
}
```

Remember the browser checks for a recent user interaction, so this code should normally be run within an event handler.


## Clipboard Events

`cut`, `copy`, and `paste` events are triggered when the user interacts with the clipboard in any way (keyboard shortcut, long tap, right-click menu, `read()` call, `write()` call, etc.) Handler code can intercept these events and send their own the [`ClipboardItem`](https://developer.mozilla.org/docs/Web/API/ClipboardItem). Alternatively, the event object passed to the handler also provides a [`clipboardData` property](https://developer.mozilla.org/docs/Web/API/ClipboardEvent/clipboardData) which can be examined or modified as necessary.

For example, you cannot normally *cut* text from a web page, but the following event handler converts a selection to uppercase text, writes it to the clipboard, and deletes it from the document:

```javascript
body.addEventListener('cut', e => {

  // selection available>
  const selection = document.getSelection();
  if (selection.type !== 'Range') return;

  // write uppercase text to clipboard
  e.clipboardData.setData(
    'text/plain',
    selection.toString().toUpperCase()
  );

  // remove selection from document
  selection.deleteFromDocument();

  // stop default cut/copy
  e.preventDefault();

});
```

The following example intercepts a paste into a `<textarea>` field with the id of `mytext` and forces it to uppercase:

```javascript
document.getElementById('mytext').addEventListener('paste', e => {

  // modify pasted text
  e.target.value = e.clipboardData.getData('text').toUpperCase();

  // stop default paste
  e.preventDefault();

});
```


## Conclusion

The Clipboard API is easy to use and has good support in all modern browsers. That said, there are plenty of edge cases to catch you out, and even organizations with plenty of resources don't always get it right *(try copying an image from a Google Docs document to your local OS!)*

I suggest you use the Clipboard API as a functional enhancement when it makes practical sense. For example, an app that allows you to copy SVG image code (XML) could show it in a `<textarea>` or a `<code>` block with CSS `user-select: all` set. Copy and paste are then possible in all browsers, including ancient applications. In browsers where `navigator.clipboard.writeText` is supported, that element can be hidden and replaced with a single button that copies the SVG code to the clipboard.

Finally, remember to test your clipboard code across a range of desktop and mobile browsers with mouse, keyboard, and touch control.

> A TIP FROM THE EDITOR: For more on JavaScript APIs, give a look at our [Four Useful Built-In JavaScript Web APIs](https://blog.openreplay.com/four-useful-built-in-javascript-web-apis/) and [Using The Native Payment Request JavaScript API](https://blog.openreplay.com/using-the-native-payment-request-javascript-api/) articles.


[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)