CodingNic

Browser APIs

Clipboard API

Browser APIs 15 min read

Clipboard API

Objectives

By the end of this chapter, you should be able to:

  • Copy text to the clipboard with navigator.clipboard.writeText()
  • Read text from the clipboard with navigator.clipboard.readText()
  • Explain why clipboard access has to happen inside a real user action

๐Ÿ’ก Why this matters: “Copy” buttons are everywhere, a code snippet, a share link, a coupon code. The Clipboard API is how a page does that without asking the user to manually select and copy the text themselves.

โš ๏ธ A note on verification: the same limitation as last lesson applies here. The Clipboard API requires a real browser, a real system clipboard, and (depending on the browser) a real user permission decision, none of which exist in this sandbox. The code below is accurate, based on the standardized Clipboard API, run it yourself in a browser to see it firsthand.

What You’re Building

A “Share this project” panel: a project thumbnail, an invite link with a copy button, and, further down, a feedback box that can paste in whatever’s currently on the clipboard.

text
share-panel/
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ script.js
โ””โ”€โ”€ project-thumbnail.png
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Share This Project</title>
</head>
<body>
  <h1>Share This Project</h1>
  <img src="project-thumbnail.png" alt="Project thumbnail" width="120" height="80">
  <p id="inviteLink">https://example.com/invite/abc123</p>
  <button id="copyBtn">Copy Invite Link</button>

  <h2>Feedback</h2>
  <textarea id="feedbackBox" placeholder="Paste your feedback here"></textarea>
  <button id="pasteBtn">Paste from Clipboard</button>

  <script src="script.js"></script>
</body>
</html>

project-thumbnail.png is just a static image sitting alongside index.html, no JavaScript touches it. Everything else from here on goes in script.js.

Step 1: Copy on Click

navigator.clipboard.writeText(text) copies text to the system clipboard. It’s promise-based, fitting directly into everything from Module 3, and it only works inside a direct response to a real user action, a click handler, never on page load or inside a timer.

javascript
const copyBtn = document.getElementById("copyBtn");
const inviteLink = document.getElementById("inviteLink");

copyBtn.addEventListener("click", async () => {
  await navigator.clipboard.writeText(inviteLink.textContent);
  copyBtn.textContent = "Copied!";
});

inviteLink.textContent (Module 1) reads the link straight off the page, so whatever’s copied always matches what’s actually displayed, there’s no separate copy of the link text to keep in sync.

Step 2: Handle Failure

Clipboard access can fail, denied permission, an insecure context (recall the HTTPS requirement from last lesson, it applies here too), a browser that doesn’t support it. Wrap it in try/catch (Module 3).

javascript
copyBtn.addEventListener("click", async () => {
  try {
    await navigator.clipboard.writeText(inviteLink.textContent);
    copyBtn.textContent = "Copied!";
  } catch (error) {
    copyBtn.textContent = "Failed to copy";
  }
});

Step 3: Reset the Button Text

“Copied!” staying on the button forever is confusing the second time someone wants to copy it. Reset it after a short delay, using setTimeout() (Module 3).

javascript
copyBtn.addEventListener("click", async () => {
  try {
    await navigator.clipboard.writeText(inviteLink.textContent);
    copyBtn.textContent = "Copied!";
  } catch (error) {
    copyBtn.textContent = "Failed to copy";
  }

  setTimeout(() => {
    copyBtn.textContent = "Copy Invite Link";
  }, 2000);
});

Two seconds later, the button goes back to its original label, ready to be clicked again.

Step 4: Add a Paste Feature

Reading the clipboard is more sensitive than writing to it, a page silently reading whatever a user last copied (a password, a private message) is an obvious privacy concern, so browsers are more cautious here, often prompting for permission every time, unlike writing.

javascript
const pasteBtn = document.getElementById("pasteBtn");
const feedbackBox = document.getElementById("feedbackBox");

pasteBtn.addEventListener("click", async () => {
  try {
    const text = await navigator.clipboard.readText();
    feedbackBox.value = text;
  } catch (error) {
    feedbackBox.placeholder = "Couldn't read the clipboard";
  }
});

Clicking “Paste from Clipboard” fills feedbackBox with whatever text is currently copied, saving the user from manually pasting with a keyboard shortcut, useful on a touch device where that’s less convenient.

The Complete script.js

javascript
const copyBtn = document.getElementById("copyBtn");
const inviteLink = document.getElementById("inviteLink");
const pasteBtn = document.getElementById("pasteBtn");
const feedbackBox = document.getElementById("feedbackBox");

copyBtn.addEventListener("click", async () => {
  try {
    await navigator.clipboard.writeText(inviteLink.textContent);
    copyBtn.textContent = "Copied!";
  } catch (error) {
    copyBtn.textContent = "Failed to copy";
  }

  setTimeout(() => {
    copyBtn.textContent = "Copy Invite Link";
  }, 2000);
});

pasteBtn.addEventListener("click", async () => {
  try {
    const text = await navigator.clipboard.readText();
    feedbackBox.value = text;
  } catch (error) {
    feedbackBox.placeholder = "Couldn't read the clipboard";
  }
});

Try It

Extend the share panel from this lesson:

  1. Build the full copyBtn handler, writing to the clipboard, updating the button text on success and failure, and resetting it after two seconds.
  2. Build the pasteBtn handler, reading the clipboard into feedbackBox.
  3. Add a check before pasting: if feedbackBox.value already has text in it, ask for confirmation (in your own logic, not a browser confirm()) before overwriting it with the pasted content.

Recap

  • navigator.clipboard.writeText(text) and .readText() are both promise-based, fitting directly into async/await, this lesson’s copy and paste buttons both used try/catch around them.
  • Reading the clipboard is more sensitive than writing to it, and browsers often prompt for permission every time.
  • Clipboard access only works inside a direct response to a real user action, like a click handler, never on page load or inside a timer.
  • Wrap clipboard calls in try/catch, they can fail for several reasons: denied permission, an insecure context, or lack of browser support.

Next lesson: the Notifications API, showing a system notification from a web page.