v145 · Web APIs · Activation & Permissions

Activation & Permissions

The clipboardchange event has two distinct gating requirements in Chrome 145: the page must have sticky activation (a recent user gesture), and reading the new clipboard value requires the clipboard-read permission. Understanding the separation between "event fires" and "value readable" is key to building correct clipboard-aware UIs.

Event vs. value access are separate concerns. The clipboardchange event fires to signal that a change occurred. Accessing the new value is a second step, gated by permission. This separation lets apps react to clipboard changes (show a "Paste" badge) without needing the full clipboard-read permission just to know something changed.

the two gates

Gate 1 — Sticky Activation

Controls whether the event fires at all. The page must have received a recent user gesture (click, keypress, touch). This prevents silent background surveillance from pages opened in background tabs and never interacted with.

A page without sticky activation will not receive clipboardchange events regardless of permission state.

Gate 2 — clipboard-read Permission

Controls whether the handler can read the clipboard value. The event fires with or without this permission, but navigator.clipboard.readText() inside the handler throws NotAllowedError if permission is denied.

When permission is prompt, the browser can show a permission dialog inside the handler because the event counts as a trusted user-gesture context.

what you get at each state

Sticky activation clipboard-read Event fires? Can read value? Typical use
✓ active granted Yes Yes (silent) Rich editor with full clipboard sync
✓ active prompt Yes Yes (prompts once) On-demand paste preview — prompt on first use
✓ active denied Yes No Show "clipboard changed" badge; manual paste only
✗ inactive any No No Background tab — no surveillance possible

correct handler pattern

  1. Attach the listener at page load (or when clipboard-related UI becomes active). No permission needed just to listen.
  2. When the event fires, query permission state before attempting readText(). A direct permission query is fast and non-blocking.
  3. If granted: call readText() silently and update UI.
  4. If prompt: call readText() inside the handler — the browser can show the permission dialog here because the event fires in a trusted context. Wrap in try/catch in case the user denies.
  5. If denied: fall back gracefully — show a "Paste" button that lets the user manually Ctrl+V, or show a generic "clipboard updated" indicator without the value.
window.addEventListener('clipboardchange', async () => {
  const { state } = await navigator.permissions.query({ name: 'clipboard-read' });

  if (state === 'granted') {
    const text = await navigator.clipboard.readText();
    updatePastePreview(text);

  } else if (state === 'prompt') {
    try {
      // Browser may show permission UI here — we're in a trusted event
      const text = await navigator.clipboard.readText();
      updatePastePreview(text);
    } catch {
      showManualPasteButton();
    }

  } else {
    // 'denied' — still useful to know something changed
    showClipboardChangedBadge();
  }
});

see also