Generating image from an element and copying it to clipboard using JavaScript

Rishi Mohan

By Rishi Mohan

on December 22, 2021

While most browsers support navigator.clipboard.writeText property which is used to write a string to clipboard, it gets little tricky when you want to copy an image to clipboard. There's no common way for copying a blob image to clipboard for all browsers.

While working on an app that generates image from a tweet and lets you save it, I wanted to add ability to let users copy the image directly to clipboard. The implementation seemed simple until I started working on it. The problem is that browsers have different specs for writing a blob to clipboard.

In this article, we'll learn to write a function that generates image from an element and saves it in clipboard. Also since browsers handle things differently, we'll see how to make copying an image to clipboard work on Safari and Chrome(and its variants).

Things to know before we start

  • Unfortunately, there's no way(or I haven't found one) to copy a blob image to clipboard in Firefox.
  • There's no way to copy multiple images in one go.

Creating an image from an element

Below is a function that utilises domtoimage library to return an image blob from the element screenshotRef.current.

We can use yarn add dom-to-image to install the package.

Instead of screenshotRef.current used in the function, we can pass the id or className of the element we want to generate the image from. More about how domtoimage works can be learned here.

1const snapshotCreator = () => {
2  return new Promise((resolve, reject) => {
3    try {
4      const scale = window.devicePixelRatio;
5      const element = screenshotRef.current; // You can use element's ID or Class here
6      domtoimage
7        .toBlob(element, {
8          height: element.offsetHeight * scale,
9          width: element.offsetWidth * scale,
10          style: {
11            transform: "scale(" + scale + ")",
12            transformOrigin: "top left",
13            width: element.offsetWidth + "px",
14            height: element.offsetHeight + "px",
15          },
16        })
17        .then((blob) => {
18          resolve(blob);
19        });
20    } catch (e) {
21      reject(e);
22    }
23  });
24};

Copying an image to clipboard in Safari

First, we'll need a check to see if the browser is Safari. You can use below check to detect Safari.

1const isSafari = /^((?!chrome|android).)*safari/i.test(
2  navigator?.userAgent
3);

Now that we have the check, we can use the below function to copy the image to clipboard.

1const copyImageToClipBoardSafari = () => {
2  if(isSafari) {
3    navigator.clipboard
4      .write([
5        new ClipboardItem({
6          "image/png": new Promise(async (resolve, reject) => {
7            try {
8              const blob = await snapshotCreator();
9              resolve(new Blob([blob], { type: "image/png" }));
10            } catch (err) {
11              reject(err);
12            }
13          }),
14        }),
15      ])
16      .then(() =>
17        // Success
18      )
19      .catch((err) =>
20        // Error
21        console.error("Error:", err)
22      );
23  }
24}

We're using navigation.clipboard.write property to write an image blob to clipboard. Inside it, we're creating a new ClipboardItem from the blob of the element which is generated from snapshotCreator() function which we created in first step.

Copying an image to clipboard in other browsers

Since this method doesn't work in Firefox, we'll need a check to make sure we're not running it on Firefox. Below condition takes care of that.

1const isNotFirefox = navigator.userAgent.indexOf("Firefox") < 0;

Now that we have the check, we'll use the same technique as we used for Safari, but this time we'll need to ask the browser for the navigator permissions.

Below is the function that does that and then copies the blob image to clipboard in Chrome browser and its variants.

1const copyImageToClipBoardOtherBrowsers = () => {
2  if(isNotFirefox) {
3    navigator?.permissions
4      ?.query({ name: "clipboard-write" })
5      .then(async (result) => {
6        if (result.state === "granted") {
7          const type = "image/png";
8          const blob = await snapshotCreator();
9          let data = [new ClipboardItem({ [type]: blob })];
10          navigator.clipboard
11            .write(data)
12            .then(() => {
13              // Success
14            })
15            .catch((err) => {
16              // Error
17              console.error("Error:", err)
18            });
19        }
20    });
21  } else {
22    alert("Firefox does not support this functionality");
23  }
24}

The difference is not much except that Chrome and its variants require asking for navigator.permissions before we can write the blob content to clipboard. The above function uses the same snapshotCreator() function from the firs step to create an image from the element.

We can combine both the functions together to have this functionality work in Safari and Chrome browser. You can check how it works on this page, just click on "Copy Image" button and then you'll be able paste the image anywhere.

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.