Back to journal
·2 min readJavaScriptClipboard ApiWeb ApisAsyncTutorial

Copy to Clipboard with JavaScript

Use the Async Clipboard API for text and images. HTTPS required, permissions matter, and `execCommand` is dead weight now.

ShareCopy failed

"Copy code" buttons show up on docs, dashboards, recovery keys. Users shouldn't have to select text by hand.

We used document.execCommand('copy'). It was sync, flaky on long strings, and permission behavior varied by browser. The Async Clipboard API fixes most of that.

Security basics

Clipboard access is sensitive. Rules you'll hit:

HTTPS only (localhost is fine). Tab must be active. Reads need explicit permission; writes are a bit looser but still gated.

Permission names you'll see: clipboard-write, clipboard-read. Check with navigator.permissions when debugging.

navigator.permissions.query({ name: "write-on-clipboard" }).then((result) => {
  if (result.state == "granted" || result.state == "prompt") {
    console.log("Write access granted!");
  }
});

Copy text

export const App = () => {
  const copyText = async () => {
    try {
      await navigator.clipboard.writeText("Some text to copy");
      console.log("Clipboard content has been copied.");
    } catch (error) {
      console.error("Failed to copy content to clipboard.");
      console.log(err.name, err.message);
    }
  };

  return (
    <div className="wrapper">
      <button className="btn" onClick={copyText}>
        copy text
      </button>
    </div>
  );
};

text to clipboard

Copy images

write() takes an array of ClipboardItem objects:

export const App = () => {
  const copyImage = async () => {
    try {
      const response = await fetch("/logo-with-shadow.png");
      const blob = await response.blob();
      await navigator.clipboard.write([
        new ClipboardItem({
          [blob.type]: blob,
        }),
      ]);
      console.log("Image copied to clipboard.");
    } catch (error) {
      console.error("Failed to copy image to clipboard.");
      console.log(error.name, error.message);
    }
  };

  return (
    <div className="wrapper">
      <button className="btn" onClick={copyImage}>
        copy image
      </button>
    </div>
  );
};

image to clipboard

Browsers sanitize HTML and block weird PNG bombs. Support for MIME types still varies. Test your target browsers.

Don't copy secrets you don't need. Tell users when something landed on the clipboard. Simple, but easy to forget.

ShareCopy failed