Copying to Clipboard using the Clipboard API
The Clipboard API is a browser-native way of interacting with a visitors clipboard.
It includes both the ability to read what’s already in a visitors clipboard (somewhat similar to a paste action) as well as the ability to write new data (similar to a copy action).
While some parts of this API have limited support, it’s pretty well supported on most modern browsers at the time of writing this.
Copying text in React
To copy text, we can use the writeText method available on navigator.clipboard
.
await navigator.clipboard.writeText('My Text');
writeText returns a promise, so we want to await that command.
Copying image data in React
The Clipboard API also supports copying image data as a PNG (as well as text or HTML) by using the write method.
We can copy image data by creating a new instance of ClipboardItem from a Blob and passing it to the write method.
const data = [new ClipboardItem({ ['image/png']: blob })];
await navigator.clipboard.write(data);
The above assumes the data Blob data being written is a PNG, where you need to ensure you pass in the appropriate type for the Blob.
If you’re looking to get a Blob from an existing image URL, you can first download the image, obtain the content type and the Blob, and then construct your ClipboardItem.
const response = await fetch('https://res.cloudinary.com/spacejelly-tutorials/image/upload/v1704743747/my-clipboard/Windows_11_Clippy_paperclip_emoji_rlnfod.png');
const contentType = response.headers.get('Content-Type');
const blob = await response.blob();
const data = [new ClipboardItem({ [contentType]: blob })];
await navigator.clipboard.write(data);
Fallback options for older browsers
The writeText and write methods both have the best support out of the Clipboard API methods, particularly writeText which is pretty well supported throughout all modern browsers, but given it’s still relatively new in web API terms, if you need to support older browsers, you may want to provide a fallback.
The execCommand method is one way to achieve this, though its considered a deprecated API.
You would first need to “select” the text such as text inside of an input then execute the copy command:
document.querySelector('#my-input').select();
document.execCommand('copy');
If trying to provide backwards compatibility though, you may want to consider using a library that handles this for you which can provide even more support, such as react-copy-to-clipboard or the library that react-copy-to-clipboard uses which is copy-to-clipboard.
For instance, if using copy-to-clipboard, you can copy text by running:
import copy from 'copy-to-clipboard';
copy('My Text');
You can even use the Clipboard API when supported with a fallback in browsers its not with:
if ( 'clipboard' in navigator ) {
await navigator.clipboard.writeText('My Text')
} else {
copy('My Text');
}
Reading from Clipboard with the Clipboard API
While writing to clipboard is likely a much more common need, the Clipboard API also gives us access to the readText and read methods to grab the top value from a visitors keyboard.
This isn’t without security considerations though, as a visitor must explicitly permit access to their Clipboard before it being read.
These are also the two methods that have the least browser support, where they are not supported in Firefox at the time of writing this.
Reading text from Clipboard in React
To read text, we can invoke the readText method:
const text = await navigator.clipboard.readText();
Once your visitor permits this action, the text value will now be stored in the text constant from the example above.
Reading image data from Clipboard in React
Reading image data from a Clipboard is a little bit more involved than text using the read method. Similar to write, read supports image data as a PNG, HTML, and text, but in our example we’ll be using image data.
The first step is to read the data.
const clipboard = await navigator.clipboard.read();
After the visitor provides permission and the Clipboard is read, we’ll end up with an array of ClipboardItems where we can then access the data based on type.
const image = await clipboard[0].getType('image/png');
In this instance, I’m grabbing the first ClipboardItem and getting the data by its type image/png
.
To read all the data copied, you can loop through the ClipboardItems.
const images = await Promise.all(clipboard.map(clipboardItem => {
return clipboardItem.getType('image/png');
}));
getType returns a promise which is why we use Promise.all to await for all images to be read. This is also assuming that each image is a PNG, where to validate the item is a PNG before trying to read it, you can first inspect the ClipboardItem’s types
property.
const images = await Promise.all(clipboard.map(clipboardItem => {
if ( !clipboardItem.types.includes('image/png') ) return;
return clipboardItem.getType('image/png');
}));
Adding Convenience for a Better Experience
While most devices should support the ability to copy values from a browser, giving visitors a way to easily copy text or data with a button or action just gives them a smoother experience when trying to interact with your app.
When implementing copy UIs, consider adding confirmation notifications, such as a little green checkmark on success, or letting them know if its failed.
The best experiences will provide convenient ways to interact while ensure your visitor gets feedback from their actions.