Async Clipboard API: reading and writing the clipboard
In one line: The Async Clipboard API — navigator.clipboard — lets you read from
and write to the system clipboard programmatically. Writing plain text requires no
permission; reads are tightly gated: Chromium may use the clipboard-read permission,
while Firefox and Safari require transient activation and the browser’s own paste prompt
(no persistent grant is stored). All operations require a secure context (HTTPS or localhost).
Writing to the clipboard
Section titled “Writing to the clipboard”Plain text (most common)
Section titled “Plain text (most common)”try { await navigator.clipboard.writeText('Hello, clipboard!');} catch (err) { console.error('Clipboard write failed:', err);}writeText requires:
- A secure context (HTTPS or
localhost). - Either a user activation or that the page has focus (the exact rule is browser-dependent, but calling from a click handler is always safe).
Rich content (images, HTML)
Section titled “Rich content (images, HTML)”const blob = new Blob(['<b>hello</b>'], { type: 'text/html' });const item = new ClipboardItem({ 'text/html': blob });await navigator.clipboard.write([item]);write() accepts an array of ClipboardItem objects. Each item can carry multiple
MIME types so the paste target can pick the richest format it understands.
Reading from the clipboard
Section titled “Reading from the clipboard”Plain text
Section titled “Plain text”const text = await navigator.clipboard.readText();Rich content
Section titled “Rich content”const items = await navigator.clipboard.read();for (const item of items) { for (const type of item.types) { const blob = await item.getType(type); // process blob }}Permission model
Section titled “Permission model”Permission behavior differs significantly by browser family.
Chromium (Chrome, Edge, Opera)
Section titled “Chromium (Chrome, Edge, Opera)”Chromium uses the clipboard-read and clipboard-write Permissions API names:
- Writing (
writeText/write): no permission prompt forwriteTextwhen called from a focused, user-activated context.write()with certain MIME types (e.g., images) may prompt forclipboard-write. - Reading (
readText/read): requires theclipboard-readpermission; a browser prompt is shown the first time. You can query the current state without triggering a prompt:
// Chromium only — clipboard-read is not a recognised name in Firefox or Safariconst { state } = await navigator.permissions.query({ name: 'clipboard-read' });// state: 'granted' | 'denied' | 'prompt'Firefox
Section titled “Firefox”Firefox does not recognise clipboard-read or clipboard-write as Permissions API
names — navigator.permissions.query({ name: 'clipboard-read' }) will reject or return
a nonsense state. Instead:
- Writing: requires transient activation (a user gesture such as a click).
- Reading: requires transient activation and triggers a one-time browser paste confirmation dialog the user must accept; there is no persistent grant.
Safari
Section titled “Safari”Safari also does not expose clipboard-read/clipboard-write to the Permissions
API. Its model is similar to Firefox:
- Writing: requires transient activation.
- Reading: requires transient activation and a user-initiated paste prompt; no persistent permission is stored.
Secure context (HTTPS)
Section titled “Secure context (HTTPS)”navigator.clipboard is only available in secure contexts (HTTPS or localhost).
On plain HTTP the property is undefined.
User-gesture and focus requirement
Section titled “User-gesture and focus requirement”Both read and write operations should be called from within a user-gesture handler or
while the document has focus. Browsers differ slightly, but calling from a click
handler is always safe. Attempting to call outside a user-activated context typically
results in a NotAllowedError.
Fallback: document.execCommand
Section titled “Fallback: document.execCommand”The legacy document.execCommand('copy') and document.execCommand('paste') work in
older browsers but are deprecated and do not support rich content. Use them only as a
last resort:
function legacyCopy(text) { const textarea = document.createElement('textarea'); textarea.value = text; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); textarea.remove();}Browser & ecosystem support
Section titled “Browser & ecosystem support”See /compatibility/ for current per-browser data.
Decision framework
Section titled “Decision framework”| Decision question | Recommended action | Rationale |
|---|---|---|
| Copy plain text on button click? | navigator.clipboard.writeText() inside a click handler. |
No permission prompt; works in all modern browsers with HTTPS. |
| Copy an image or HTML to clipboard? | navigator.clipboard.write([new ClipboardItem(...)]). |
Allows multi-format payloads; the paste target picks the best type. |
| Read clipboard contents? | Always call from a user-gesture handler. In Chromium you can query clipboard-read permission first; in Firefox and Safari rely on transient activation and the browser’s own paste prompt — do not use navigator.permissions.query for this. |
Permission model is browser-family-specific; transient activation is the portable baseline. |
| Need to support older browsers? | Feature-detect navigator.clipboard; fall back to execCommand. |
execCommand is deprecated but still works in older engines. |
| Clipboard in a Service Worker or background context? | Not possible — clipboard access requires a focused document. | By spec and browser policy, clipboard is not available in background contexts. |
Practical checklist
Section titled “Practical checklist”- Serve the page over HTTPS —
navigator.clipboardis undefined on plain HTTP. - Call clipboard methods from user-gesture handlers or while the document has focus.
- Use
writeTextfor plain text; usewritewithClipboardItemfor rich content. - Always call read methods from a user-gesture handler. In Chromium, optionally query the
clipboard-readpermission state first; in Firefox and Safari, depend on transient activation and the browser’s paste prompt —navigator.permissions.query({ name: 'clipboard-read' })is not portable across browser families. - Catch errors from all clipboard calls — permissions can be denied or revoked.
- Provide a
document.execCommandfallback for browsers that do not support the Async Clipboard API.