iOS Safari’s Clipboard Permission Dialog Has a One-Line Fix
A permission dialog on copy breaks UI flow and confuses users. This technique removes the dialog without any polyfill, keeping async clipboard writes seamless on iOS Safari.
iOS Safari enforces a stricter clipboard security model than other browsers: `navigator.clipboard.write()` must execute synchronously within a user gesture, or the system shows a confirmation dialog. The common mistake is awaiting an async fetch before constructing the `ClipboardItem`, which drops the gesture context and triggers the prompt. The fix is to construct the `ClipboardItem` immediately inside the click handler and supply a `Promise<Blob>` as the value. The browser reserves the write at that moment and resolves the data later, skipping the permission popup. A wrapper function that accepts a string, a Promise, or a lazy function normalizes this pattern and falls back to `execCommand('copy')` when the Clipboard API is unavailable or fails.
The permission dialog is not a blanket iOS restriction but a narrow timing check — the browser only cares whether the `ClipboardItem` is created during the gesture, not when the data actually arrives.
This behavior turns the `ClipboardItem` constructor into a capability-registration point: call it synchronously to claim the clipboard slot, then fill in the payload later.
Many clipboard wrappers in the wild still `await` data before calling `write()`, which means a large portion of web apps are unnecessarily showing permission prompts to iOS users.
There's also a pitfall on the read side: clipboard.read() also triggers the permission prompt. Switching to listening for the paste event and picking image/* from clipboardData.items to get a File bypasses that API. On the write side, we happened to step on the wrong example you mentioned — will fix it later.
Productive exchange [handshake]