Async Clipboard API "ClipboardItem is not defined" - Reactjs copy image to Clipboard

Viewed 5381

I'm working on React js, I created my app with create-react-app using npm. I was trying to build a button that takes an image and writes it to the clipboard. Fourtunately I found this npm library that seems to work fine! But keeps me thinking why I couldn't use the ¿built-in? Asynchronous Clipboard API to copy the image (the text copy works fine). I read a really enlightening guide here, and kept reading other great guide here, so I tried all the codes suggested, there and in other pages (despite they don't seem to really change the functionality, I got to try). I came with the same error in every try that impedes to compile: "'ClipboardItem' is not defined no-undef". One code for example was this one:

const response = await fetch('valid img url of a png image');
const blob = await response.blob();
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob})]);

It seems to be simple, easy to follow. The problem is when you need to put the data in a form the Clipboard can read it, make it a blob, because I need the ClipboardItem constructor, and my app seems to be unable to recognize it as such. Keeps returning ClipboardItem is not defined or, if I somehow define it, says it's not a constructor, of course. I tried with other constructors like Blob(), but had the same problem. The last thing kept me thinking that, since I'm new in the programming world, if there is something kinda basic I don't know of the interaction of Web Apis like this one with node or Reactjs, and if there is a solution, of course! Thanks in advance, you guys are great!

Edit: adding the whole component code as requested:

import React from "react";
  
function TestingClipAPI () { 

  async function handleScreenshot () {
    const response = await fetch('https://i.postimg.cc/d0hR8HfP/telefono.png');
    const blob = await response.blob();
    await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob})]);
  };
  
  return (
    <div>
     <button onClick={handleScreenshot} id="buttonID">test</button>
    </div>
  )
};

export default TestingClipAPI;

Possible issue: This might be because of CRA (Create-React-App) config - similar issue. Something like the library linked can be done, create a canvas and copy the image from there.

Solution or a way to make it work anyway: make a call this way before using ClipboardItem:

const { ClipboardItem } = window;

Note: this also works with other constructors like toBlob and HTMLCanvasElement that had the same issue.

3 Answers

Things to look for:

  1. Browser support Clipboard
  2. Secure origin on HTTPS or localhost. See this post.
  3. How the function is being called - in the OP's case - onClick & asynchronous.

The issue is that onClick are not asynchronous by default and you are not awaiting the response and you also have a typo in navigator.clipboard.

  const handleScreenshot = async () => {
    try {
      const response = await fetch(
        "https://i.postimg.cc/d0hR8HfP/telefono.png"
      );
      const blob = await response.blob();
      await navigator.clipboard.write([
        new ClipboardItem({ "image/png": blob }),
      ]);
    } catch (err) {
      console.error(err);
    }
  }

return (
  <button onClick={async () => await handleScreenshot()} id="buttonID">
    test
  </button>
);

There are tradeoff between inline function and below are alternatives. I'd personally use the latter method.

function handleScreenshot() {
  async function screenShot() {
    try {
      const response = await fetch(
        "https://i.postimg.cc/d0hR8HfP/telefono.png"
      );
      const blob = await response.blob();
      await navigator.clipboard.write([
        new ClipboardItem({ "image/png": blob }),
      ]);
    } catch (err) {
      console.error(err);
    }
  }
  screenShot();
}

return (
  <button onClick={handleScreenshot} id="buttonID">
    test
  </button>
);

Lastly, you can return a chained promise.

Simply add window in front of ClipboardItem like the following

window.ClipboardItem(...)
Related