Skip to content

API in Capabilities

Async Clipboard API

Published

Newly available since 2024-06W3C draft

The Async Clipboard API, navigator.clipboard, reads and writes the system clipboard through promises, replacing document.execCommand('copy') and ('paste'). Text goes through writeText() and readText(); images, HTML, and multi-format payloads travel as ClipboardItem objects through write() and read().

Chrome 66 shipped writeText() and readText(), and Chrome 76 added read(), write(), and ClipboardItem; Edge 79 and Samsung Internet 12.0 follow Chromium. Firefox added writeText() in 63, readText() in 125, and read(), write(), and ClipboardItem in 127. Safari 13.1 on macOS and 13.4 on iOS shipped all four methods at once (BCD api.Clipboard). The API exists only in a Window on a secure context: on http:// origins other than localhost, navigator.clipboard is undefined, and Chromium rejects reads from workers with NotAllowedError.

navigator.clipboard.readText()
navigator.clipboard.read()
navigator.clipboard.read(formats)
navigator.clipboard.writeText(data)
navigator.clipboard.write(data)
new ClipboardItem(items)
new ClipboardItem(items, options)
clipboardItem.getType(type)
ClipboardItem.supports(type)

readText() returns Promise<DOMString>; read() returns Promise<sequence<ClipboardItem>> whose items carry type names only, with the bytes fetched when getType() is called; writeText() and write() return Promise<undefined>. Reads need transient user activation in Firefox 125+ and Safari 13.1+, and either activation or the clipboard-read permission in Chrome. Writes need transient activation in Firefox 63+ and Safari 13.1+, and since Chrome 107 either activation or the clipboard-write permission (before 107 the permission alone decided). ClipboardItem.supports() is static and synchronous; it exists in Chrome 121, Firefox 127, and Safari 18.4.

The three method arguments and the two ClipboardItem constructor arguments cover every input the specification defines.

Parameter Type Required Description
data (writeText) DOMString Yes Written as a text/plain;charset=utf-8 blob. On Windows the browser replaces \n with \r\n first.
data (write) sequence<ClipboardItem> Yes Chrome 76+ and Firefox 127+ accept exactly one item; an array with two or more items rejects with NotAllowedError (BCD api.Clipboard.write).
formats (read) ClipboardUnsanitizedFormats No One member, unsanitized: sequence<DOMString>, asking for raw rather than sanitized bytes. Chrome 122 honours it for text/html only; any other type rejects.
items (constructor) record<DOMString, ClipboardItemData> Yes MIME type to Promise<Blob or DOMString> (a plain Blob or string is wrapped). Keys prefixed with "web " are custom formats (Chrome 104).
options.presentationStyle "unspecified", "inline", or "attachment" No Default "unspecified"; a hint to paste targets. Chrome ignores it; Firefox 127 and Safari 13.1 expose it on read items.
type (getType, supports) DOMString Yes A MIME type, optionally with the "web " prefix.

A ClipboardItem exposes types (a frozen array of its MIME type strings) and presentationStyle. The mandatory types every browser must accept are text/plain, text/html, and image/png; image/svg+xml is optional and ships in Chrome 124.

Every method returns a rejected promise rather than throwing, except the ClipboardItem constructor and the synchronous part of getType().

Exception Condition
NotAllowedError read() or readText() when “check clipboard read permission” fails (no transient activation and no granted clipboard-read); write() or writeText() when the write check fails; a blob whose type is outside the mandatory and optional types; sanitization that did not complete; a representation promise that rejected; read(formats) naming an unsanitized type outside the optional unsanitized list; more than one ClipboardItem in Chrome and Firefox; the document not having focus in Chromium; a clipboard-read or clipboard-write Permissions Policy denial.
NotFoundError readText() when the clipboard holds no text/plain representation, or decoding that representation fails.
TypeError new ClipboardItem() with an empty items record, a key that does not parse as a MIME type, or the same type listed twice; getType() with an unparsable type.
InvalidStateError getType() on an item from read() after the system clipboard changed, or on an item whose originating system entry is gone.
DataError Chromium only: a ClipboardItemData promise that could not be read or decoded, or clipboard contents that changed while a paste event was being handled.

Firefox adds a browser-level gate the specification does not name: read() and readText() show a paste prompt the user must accept, suppressed only when the clipboard content is same-origin (BCD api.Clipboard.read). A declined prompt rejects with NotAllowedError.

Each example feature-detects the exact method it needs and shows the branch taken when it is missing. Run the write examples from a click handler so transient activation is still valid.

Copying text with a hidden-textarea fallback

Section titled “Copying text with a hidden-textarea fallback”

navigator.clipboard.writeText() covers Chrome 66+, Firefox 63+, and Safari 13.1+; everything older, and any http:// page, needs the deprecated document.execCommand('copy'), which still works in those engines for plain text only.

async function copyText(text) {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
return 'async';
}
const area = document.createElement('textarea');
area.value = text;
area.setAttribute('readonly', '');
area.style.position = 'fixed';
area.style.opacity = '0';
document.body.append(area);
area.select();
const ok = document.execCommand('copy');
area.remove();
return ok ? 'legacy' : 'failed';
}
document.querySelector('#copy').addEventListener('click', async () => {
const result = await copyText(location.href);
console.log(`copied via ${result}`);
});

The function returns which path ran so the UI can say “Copied” only when one of them succeeded; execCommand returns false instead of rejecting when the browser refuses.

Writing a PNG and an HTML fallback in one item

Section titled “Writing a PNG and an HTML fallback in one item”

A single ClipboardItem can carry several representations; the paste target picks the richest one it understands. Check ClipboardItem.supports() where it exists (Chrome 121, Firefox 127, Safari 18.4) and treat its absence as “mandatory types only”.

async function copyChart(canvas, captionHtml) {
if (!navigator.clipboard?.write || !('ClipboardItem' in window)) {
return copyText(captionHtml.replace(/<[^>]+>/g, ''));
}
const png = new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
const items = { 'image/png': png };
const htmlOk = typeof ClipboardItem.supports !== 'function' || ClipboardItem.supports('text/html');
if (htmlOk) {
items['text/html'] = new Blob([captionHtml], { type: 'text/html' });
}
try {
await navigator.clipboard.write([new ClipboardItem(items)]);
} catch (err) {
if (err.name !== 'NotAllowedError') throw err;
return copyText(captionHtml.replace(/<[^>]+>/g, ''));
}
}

Passing the toBlob promise directly, rather than awaiting it first, lets the browser start the write inside the same gesture and fill in the bytes later; Safari 13.1 is the engine that depends on this ordering (web.dev, Unblocking clipboard access).

Reading an image from the clipboard without a Chromium-only permission query

Section titled “Reading an image from the clipboard without a Chromium-only permission query”

Only Chromium recognises clipboard-read as a Permissions API name; navigator.permissions.query({ name: 'clipboard-read' }) throws TypeError in Firefox and Safari. Skip the query, call read() inside the gesture, and branch on the rejection.

document.querySelector('#paste').addEventListener('click', async () => {
if (!navigator.clipboard?.read) {
document.querySelector('#paste-area').focus(); // let the user press Ctrl+V / Cmd+V
return;
}
try {
const items = await navigator.clipboard.read();
const item = items.find((i) => i.types.includes('image/png'));
if (!item) {
console.log('no PNG on the clipboard; types were', items.flatMap((i) => i.types));
return;
}
const blob = await item.getType('image/png');
document.querySelector('#preview').src = URL.createObjectURL(blob);
} catch (err) {
if (err.name === 'NotAllowedError') {
document.querySelector('#paste-area').focus();
return;
}
throw err;
}
});

Focusing a contenteditable or textarea and listening for the paste event is the portable fallback: the paste event’s clipboardData.files delivers the same PNG in every engine without any permission.

Specifications

SpecificationStatus
Async Clipboard APIW3C draft
Clipboard API and events: read() methodW3C
Clipboard API and events: write() methodW3C
  • Legend
  • Yes
  • Partial
  • Flag
  • No
  • Unknown
Browser / PlatformSupportVersionsConfidenceSourceNotes
Chrome (Desktop)Yes76highsource1
Chrome (Android)Yes76highsource23
Edge (Desktop)Yes79highsource45
Firefox (Desktop)Yes127highsource678
Firefox (Android)Yes127highsource9101112
Safari (macOS)Yes13.1highsource—
Safari (iOS)Yes13.4highsource13
Samsung InternetYes12.0highsource1415
WebView (Android)Yes76highsource1617
  1. The user must grant the `clipboard-read` permission.
  2. The user must grant the `clipboard-read` permission.
  3. Derived by browser-compat-data mirroring from Chrome.
  4. The user must grant the `clipboard-read` permission.
  5. Derived by browser-compat-data mirroring from Chrome.
  6. This method must be called within user gesture event handlers.
  7. A paste prompt is displayed when the clipboard is read. If the clipboard contains same-origin content, the prompt is suppressed.
  8. Web extensions with the `clipboardRead` permission in their manifest can read data without the paste prompt displaying. Prior to Firefox 147, extensions without this permission were unable to read clipboard data.
  9. This method must be called within user gesture event handlers.
  10. A paste prompt is displayed when the clipboard is read. If the clipboard contains same-origin content, the prompt is suppressed.
  11. Web extensions with the `clipboardRead` permission in their manifest can read data without the paste prompt displaying. Prior to Firefox 147, extensions without this permission were unable to read clipboard data.
  12. Derived by browser-compat-data mirroring from Firefox.
  13. Derived by browser-compat-data mirroring from Safari.
  14. The user must grant the `clipboard-read` permission.
  15. Derived by browser-compat-data mirroring from Chrome Android.
  16. The user must grant the `clipboard-read` permission.
  17. Derived by browser-compat-data mirroring from Chrome Android.

Source data: /compatibility/clipboard.json · Global usage: 93 % (StatCounter 2026-05)

Source: spec · MDN · Last verified 2026-10-03 · Confidence: high (computed from sources)