Question

How to Copy Text to the Clipboard in JavaScript

javascriptclipboardcopy-paste

Question

How can I copy text to the user's clipboard in JavaScript in a way that works across multiple browsers?

Related question: how do web applications such as Trello access the user's clipboard?

Short Answer

By the end of this page, you will understand how clipboard copying works in JavaScript, when to use the modern Clipboard API, when a fallback is needed, and how to implement a reliable copy-to-clipboard function for real applications.

Concept

Copying to the clipboard means asking the browser to place some text into the user's system clipboard so it can be pasted elsewhere.

In modern JavaScript, the preferred way to do this is the Clipboard API, usually through navigator.clipboard.writeText(). This is asynchronous and returns a Promise, which means the browser may allow or reject the action depending on security rules.

This concept matters because copy actions are common in real applications:

  • copying invite links
  • copying API keys
  • copying code snippets
  • copying shareable URLs
  • copying tracking numbers or IDs

There is one important rule: browsers treat clipboard access as a sensitive permission. For security reasons, clipboard writes usually must happen in response to a user action such as:

  • a button click
  • a keyboard shortcut
  • a menu interaction

Older code often used document.execCommand('copy') with a selected text field. That approach was widely used for years, but it is considered legacy. It may still be used as a fallback for older browsers.

So the main idea is:

  1. Try the modern Clipboard API.
  2. If it is unavailable, use a fallback approach.
  3. Trigger clipboard copying from a user interaction.

Mental Model

Think of the clipboard like a small shared notepad owned by the operating system.

Your JavaScript code cannot freely scribble on that notepad whenever it wants. The browser acts like a security guard:

  • If the user clearly asked for it, such as clicking a Copy button, the guard usually allows it.
  • If the page tries to copy silently in the background, the guard may block it.

The modern Clipboard API is like politely handing the browser a note and asking, "Please put this on the clipboard." The old fallback method is like temporarily writing the text into a hidden input, selecting it, and using the browser's older copy command.

Take Quiz

Syntax and Examples

Modern approach: Clipboard API

async function copyText(text) {
  try {
    await navigator.clipboard.writeText(text);
    console.log('Text copied successfully');
  } catch (error) {
    console.error('Failed to copy text:', error);
  }
}

copyText('Hello, world!');

How it works

  • navigator.clipboard gives access to the Clipboard API.
  • writeText(text) tries to copy the given string.
  • await pauses until the copy operation succeeds or fails.
  • try...catch handles permission problems or unsupported browsers.

Typical usage with a button

<button id="copyBtn">Copy link</button>
<script>
  document.getElementById('copyBtn').addEventListener('click', async () => {
    try {
      await navigator.clipboard.writeText('https://example.com/share/123');
      alert('Copied!');
    } catch (error) {
      alert('Copy failed');
    }
  });
</script>

This is the most common pattern because the copy action is triggered by a user click.

Fallback for older browsers

function fallbackCopyText(text) {
  const textArea = document.createElement('textarea');
  textArea.value = text;

  textArea.style.position = 'fixed';
  textArea.style.left = '-9999px';
  textArea.style.top = '0';

  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    const successful = document.execCommand('copy');
    console.log(successful ? 'Copied' : 'Copy command failed');
  } catch (error) {
    console.error('Fallback copy failed', error);
  }

  document.body.removeChild(textArea);
}

Combined modern + fallback function

async function copyToClipboard(text) {
  if (navigator.clipboard && window.isSecureContext) {
    return navigator.clipboard.writeText(text);
  }

  const textArea = document.createElement('textarea');
  textArea.value = text;
  textArea.style.position = 'fixed';
  textArea.style.left = '-9999px';
  textArea.style.top = '0';

  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    document.execCommand('copy');
  } finally {
    document.body.removeChild(textArea);
  }
}

This combined version is useful when you want broad browser support.

Step by Step Execution

Consider this example:

async function copyToClipboard(text) {
  await navigator.clipboard.writeText(text);
  console.log('Copied:', text);
}

copyToClipboard('ABC123');

Step by step

  1. The function copyToClipboard is called with 'ABC123'.
  2. Inside the function, navigator.clipboard.writeText(text) is executed.
  3. The browser checks whether clipboard writing is allowed.
  4. If allowed, the text 'ABC123' is placed into the system clipboard.
  5. Because writeText() returns a Promise, await waits for completion.
  6. After success, console.log('Copied:', text) runs.
  7. If the browser blocks the action, the Promise rejects and the function throws an error unless you catch it.

Safer traced example

async function copyToClipboard(text) {
  try {
    console.log('Trying to copy...');
    await navigator.clipboard.writeText(text);
    console.log('Success');
  } catch (error) {
    console.log('Failed');
    console.error(error);
  }
}

Possible flow:

  • User clicks a button.
  • Function starts.
  • Browser receives clipboard request.
  • Copy succeeds.
  • Success message is shown.

Or:

  • User triggers function in an unsupported environment.
  • Browser rejects the request.
  • Error is handled in catch.

Real World Use Cases

Clipboard copying appears in many common features:

  • Share links: copy a product, profile, or invitation URL.
  • Code snippet pages: copy sample code with one click.
  • Dashboards: copy IDs, tokens, or order numbers.
  • Admin tools: copy generated passwords or setup commands.
  • Documentation sites: copy terminal commands.
  • Messaging tools: copy message permalinks or room links.

Example: copying an API key from a settings page.

document.getElementById('copyKey').addEventListener('click', async () => {
  const apiKey = 'sk_live_123456';

  try {
    await navigator.clipboard.writeText(apiKey);
    console.log('API key copied');
  } catch (error) {
    console.error('Could not copy API key');
  }
});

In practice, developers usually also show feedback such as:

  • Copied!
  • Press Ctrl+C manually
  • Copy failed
Take Quiz

Real Codebase Usage

In real projects, developers usually wrap clipboard logic in a reusable utility instead of repeating it everywhere.

Common pattern: utility function

export async function copyText(text) {
  if (navigator.clipboard && window.isSecureContext) {
    await navigator.clipboard.writeText(text);
    return true;
  }

  const textArea = document.createElement('textarea');
  textArea.value = text;
  textArea.style.position = 'fixed';
  textArea.style.left = '-9999px';
  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    return document.execCommand('copy');
  } finally {
    document.body.removeChild(textArea);
  }
}

Guard clause pattern

Developers often check support early:

if (!navigator.clipboard && !document.execCommand) {
  console.error('Clipboard support is not available');
}

UI feedback pattern

async function handleCopy(text) {
  try {
    await copyText(text);
    showToast('Copied to clipboard');
  } catch {
    showToast('Unable to copy');
  }
}

Validation pattern

async function copyIfNotEmpty(text) {
  if (!text) {
    throw new Error('Nothing to copy');
  }

  await navigator.clipboard.writeText(text);
}

In production code, the main concerns are:

  • browser support
  • permission/security restrictions
  • user feedback
  • reusable helper functions
  • graceful fallback behavior

Common Mistakes

1. Calling clipboard code without a user action

Many browsers block this:

navigator.clipboard.writeText('secret text');

If this runs automatically on page load, it may fail.

Better

document.getElementById('copyBtn').addEventListener('click', () => {
  navigator.clipboard.writeText('secret text');
});

2. Forgetting to handle Promise errors

Broken example:

navigator.clipboard.writeText('hello');
console.log('Copied');

This logs Copied even if the copy operation fails.

Better

navigator.clipboard.writeText('hello')
  .then(() => console.log('Copied'))
  .catch((error) => console.error('Copy failed', error));

3. Assuming navigator.clipboard always exists

Broken example:

await navigator.clipboard.writeText('test');

On unsupported browsers, this throws an error.

Better

if (navigator.clipboard) {
  await navigator.clipboard.writeText('test');
} else {
  fallbackCopyText('test');
}

4. Using execCommand('copy') as if it were the modern standard

It still appears in older code, but it is legacy behavior. Prefer the Clipboard API first.

5. Not cleaning up fallback elements

Broken example:

const textArea = document.createElement('textarea');
document.body.appendChild(textArea);

If you never remove it, hidden elements accumulate.

Better

try {
  document.execCommand('copy');
} finally {
  document.body.removeChild(textArea);
}

Comparisons

ApproachBest forProsCons
navigator.clipboard.writeText()Modern browsersClean API, Promise-based, standard approachRequires secure context and user permission rules
document.execCommand('copy')Older browser fallbackWorks in many older setupsLegacy approach, more awkward, requires selection trick

Promise style vs async/await

StyleExampleNotes
Promise chainingnavigator.clipboard.writeText(text).then(...).catch(...)Good if you already use Promise chains
async/awaitawait navigator.clipboard.writeText(text)Usually easier for beginners to read

Copying text vs reading clipboard

TaskMethodNotes
Write text to clipboardnavigator.clipboard.writeText(text)Common for copy buttons
Read text from clipboardnavigator.clipboard.readText()Usually more restricted and permission-sensitive

If your goal is just to copy text, writeText() is the main method to learn.

Take Quiz

Cheat Sheet

Modern copy

await navigator.clipboard.writeText('Hello');

Safe pattern

async function copyToClipboard(text) {
  if (navigator.clipboard && window.isSecureContext) {
    await navigator.clipboard.writeText(text);
    return;
  }

  const textArea = document.createElement('textarea');
  textArea.value = text;
  textArea.style.position = 'fixed';
  textArea.style.left = '-9999px';
  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    document.execCommand('copy');
  } finally {
    document.body.removeChild(textArea);
  }
}

Key rules

  • Prefer navigator.clipboard.writeText().
  • Usually trigger copying from a user action.
  • Handle errors with try...catch or .catch().
  • Use a fallback only if needed.
  • execCommand('copy') is legacy.
  • Secure context matters for the modern API.

Common checks

if (navigator.clipboard && window.isSecureContext) {
  // use modern API
}

Typical button pattern

button.addEventListener('click', async () => {
  await copyToClipboard('some text');
});

FAQ

Why does navigator.clipboard.writeText() fail sometimes?

It can fail because of browser security rules, missing permissions, unsupported browsers, or because the action was not triggered by the user.

Do I need HTTPS for clipboard copying?

For the modern Clipboard API, usually yes. It commonly requires a secure context.

Is document.execCommand('copy') deprecated?

It is considered a legacy approach. Use the Clipboard API first, and keep execCommand only as a fallback if necessary.

Can I copy text without showing an input field?

Yes. With navigator.clipboard.writeText(), you do not need a visible input. Older fallback methods often create a temporary hidden textarea.

Can JavaScript copy to the clipboard on page load?

Usually this is blocked or unreliable. Browsers prefer clipboard writes to happen after a user interaction such as a click.

How do apps like Trello access the clipboard?

They typically use browser clipboard features triggered by user actions such as keyboard shortcuts, focused inputs, paste events, or explicit copy commands.

Should I use async/await or .then() for clipboard code?

Either works. async/await is often easier to read, especially for beginners.

Take Quiz

Mini Project

Description

Build a small copy-link tool that lets a user click a button to copy a shareable URL. This demonstrates the modern Clipboard API, a fallback for older browsers, and user feedback after the copy attempt.

Goal

Create a working copy button that copies a URL and shows whether the operation succeeded or failed.

Requirements

  • Create a button that starts the copy action.
  • Copy a fixed URL string to the clipboard.
  • Use the modern Clipboard API when available.
  • Add a fallback for browsers that do not support navigator.clipboard.
  • Show a success or failure message to the user.
Take Quiz

Keep learning

Related questions

View more