The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Copy plain textawait navigator.clipboard.writeText(text)View examples
Read plain textconst text = await navigator.clipboard.readText()View examples
Write multiple formatsawait navigator.clipboard.write([new ClipboardItem({ 'text/plain': plainBlob, 'text/html': htmlBlob })])View examples
Inspect clipboard itemsconst items = await navigator.clipboard.read()View examples
Check a write typeClipboardItem.supports('image/png')View examples
Customize a copyevent.clipboardData.setData('text/plain', value)View examples
Inspect a pasteconst text = event.clipboardData.getData('text/plain')View examples
Replace default pasteevent.preventDefault()View examples
Detect API availabilityif (isSecureContext && navigator.clipboard) { /* enhance */ }View examples
Delegate clipboard accessPermissions-Policy: clipboard-read=(self), clipboard-write=(self)View examples
Open the share chooserawait navigator.share({ title, text, url })View examples
Validate file sharingnavigator.canShare?.({ files })View examples
Register a PWA share target"share_target": { "action": "/share/", "method": "POST", "params": { "text": "text" } }View examples

Clipboard and sharing features cross a privacy boundary between a page and the user's device. Offer them through clearly labeled controls, call protected APIs directly from a user action, and handle rejection without implying that data was copied or shared. Prefer plain text unless richer formats are necessary, treat pasted or received content as untrusted input, and preserve an ordinary fallback such as selectable text, a link, or a download when platform integration is unavailable.

Step by step

Detailed examples

01

Copy and read plain text through explicit controls

navigator.clipboard is restricted to secure contexts and protected by browser permission and activation rules. writeText and readText return promises, so success feedback belongs after fulfillment and failures need a useful alternative. Clipboard reads are more sensitive than writes and may prompt, require focused-document interaction, or be denied outright. Invoke the operation directly in the click or paste flow instead of after unrelated asynchronous work that may consume transient activation. Never poll the clipboard, and do not overwrite it on page load.

Copy a value and report the real result
const button = document.querySelector('#copy');
const status = document.querySelector('#copy-status');

button.addEventListener('click', async () => {
  try {
    await navigator.clipboard.writeText('https://example.com/report/42');
    status.textContent = 'Link copied.';
  } catch (error) {
    status.textContent = 'Copy unavailable. Select the link and copy it manually.';
  }
});
Back to quick reference ↑
02

Offer rich clipboard formats with a plain-text representation

write and read exchange ClipboardItem objects keyed by MIME type. When copying rich HTML, include text/plain so destinations that do not accept HTML still receive useful content. Feature support varies by type and browser; ClipboardItem.supports can test write support, while a rejected promise remains the authoritative outcome. On read, inspect item.types, request only a type you understand with getType, constrain blob sizes, and treat HTML, SVG, images, and files as hostile input. Never inject pasted HTML with innerHTML unless a well-reviewed sanitizer and content policy are in place.

Copy safe plain text plus a small HTML representation
async function copyCitation() {
  const plain = 'Platform APIs — https://example.com/platform';
  const html = '<p><strong>Platform APIs</strong> — <a href="https://example.com/platform">source</a></p>';
  const item = new ClipboardItem({
    'text/plain': new Blob([plain], { type: 'text/plain' }),
    'text/html': new Blob([html], { type: 'text/html' })
  });
  await navigator.clipboard.write([item]);
}
Back to quick reference ↑
03

Customize user-initiated copy, cut, and paste synchronously

copy, cut, and paste events expose an event-scoped DataTransfer through clipboardData. A handler can inspect available types, set copy or cut representations, or replace paste insertion. Call preventDefault only when your handler fully implements the expected result; a cut handler must also remove the selected content appropriately. Data access is intended inside the event handler, and a synthetic ClipboardEvent cannot read from or write to the real system clipboard. Preserve normal keyboard shortcuts and avoid silently changing copied URLs or commands, which can enable phishing or self-XSS.

Normalize pasted text in one designated field
const field = document.querySelector('#slug');
field.addEventListener('paste', event => {
  const text = event.clipboardData.getData('text/plain');
  if (!text) return;

  event.preventDefault();
  const normalized = text.trim().toLowerCase().replace(/[^a-z0-9-]+/g, '-');
  field.setRangeText(normalized, field.selectionStart, field.selectionEnd, 'end');
  field.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertFromPaste', data: normalized }));
});
Back to quick reference ↑
04

Design for focus, user activation, policy, and denial

Clipboard access depends on a secure context, an active and focused document, user-agent policy, user activation, and sometimes a permission grant. These details differ across engines, and Permissions API descriptors for clipboard access are not uniformly exposed; feature detection plus handling NotAllowedError is more portable than blocking the UI on a permission query. Embedded documents may additionally need clipboard-read or clipboard-write delegated by the Permissions-Policy response header and an iframe allow attribute. Keep the initiating button visible, explain what will be copied or read, and provide selectable content or a standard paste field when access is unavailable.

Enhance a copy control without depending on permission preflight
<label for="share-link">Share link</label>
<input id="share-link" value="https://example.com/guide" readonly>
<button id="copy-link" type="button">Copy link</button>
<p id="copy-result" aria-live="polite"></p>
<script>
  const copyButton = document.querySelector('#copy-link');
  const shareLink = document.querySelector('#share-link');
  const copyResult = document.querySelector('#copy-result');
  copyButton.addEventListener('click', async () => {
    try {
      await navigator.clipboard.writeText(shareLink.value);
      copyResult.textContent = 'Copied.';
    } catch {
      shareLink.select();
      copyResult.textContent = 'Press Ctrl+C or Command+C to copy the selected link.';
    }
  });
</script>
Back to quick reference ↑
05

Share progressively and receive shares defensively

navigator.share opens a user-agent chooser for text, URLs, or supported files. It requires a secure context, transient user activation, and permission from the web-share Permissions Policy; only one share can be active at a time. Use canShare with the complete candidate payload, especially files, but still handle share rejection. AbortError commonly represents cancellation and should not be announced as a failure. The API deliberately does not reveal installed targets or the chosen target. To receive shares, an installable web app can declare share_target in its manifest; validate POST fields, MIME types, extensions, byte limits, and authentication server-side, assign new storage names, and redirect after processing. A copy-link fallback works more broadly than platform sharing.

Share a page, then fall back to copying its URL
const data = { title: document.title, text: 'Useful reference', url: location.href };

document.querySelector('#share').addEventListener('click', async () => {
  try {
    if (navigator.share && (!navigator.canShare || navigator.canShare(data))) {
      await navigator.share(data);
      return;
    }
    await navigator.clipboard.writeText(data.url);
    document.querySelector('#status').textContent = 'Link copied instead.';
  } catch (error) {
    if (error.name !== 'AbortError') {
      document.querySelector('#status').textContent = 'Use the visible link to share this page.';
    }
  }
});
Declare an installed web app as a POST share target
{
  "share_target": {
    "action": "/share-target/",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "title": "title",
      "text": "text",
      "url": "url",
      "files": [{ "name": "files", "accept": ["image/png", "image/jpeg"] }]
    }
  }
}

Note: Manifest accept values are hints, not server-side validation. Treat every received field and uploaded byte as untrusted.

Back to quick reference ↑

Local code tester

Try share with a copy fallback

Use a direct user action to open the platform share chooser when supported or copy the visible URL when it is not.

Runs in your browser
Preview

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. World Wide Web ConsortiumClipboard API and Eventsw3.org
  2. World Wide Web ConsortiumWeb Share APIw3.org
  3. Web Incubator Community GroupWeb Share Target APIwicg.github.io
  4. MDN Web DocsClipboard APIdeveloper.mozilla.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback