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.
share-panel/
โโโ index.html
โโโ script.js
โโโ project-thumbnail.png
<!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.
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).
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).
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.
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
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:
- Build the full
copyBtnhandler, writing to the clipboard, updating the button text on success and failure, and resetting it after two seconds. - Build the
pasteBtnhandler, reading the clipboard intofeedbackBox. - Add a check before pasting: if
feedbackBox.valuealready has text in it, ask for confirmation (in your own logic, not a browserconfirm()) before overwriting it with the pasted content.
Recap
navigator.clipboard.writeText(text)and.readText()are both promise-based, fitting directly intoasync/await, this lesson’s copy and paste buttons both usedtry/catcharound 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.