JavaScript Clipboard API
Aprenda a Clipboard API assíncrona do JavaScript — copie e leia texto com navigator.clipboard, manipule dados ricos com ClipboardItem e entenda os requisitos de contexto seguro e permissões.
A Clipboard API moderna, exposta por meio de navigator.clipboard, é a forma assíncrona e baseada em promises de ler e gravar no clipboard do sistema. Ela substitui a abordagem antiga e síncrona com document.execCommand('copy') por métodos que retornam promises, combinando naturalmente com async/await. A API é distinta — mas frequentemente usada junto — dos eventos de clipboard cut, copy e paste: os eventos permitem interceptar o que o usuário faz, enquanto navigator.clipboard permite que seu código inicie ações de clipboard diretamente.
Gravando Texto no Clipboard
A tarefa mais comum é copiar texto. navigator.clipboard.writeText(text) aceita uma string, grava-a no clipboard e retorna uma Promise que resolve quando a gravação é bem-sucedida e rejeita quando falha.
Como retorna uma promise, a forma mais limpa de utilizá-la é dentro de uma função async com try...catch, para que você possa dar feedback ao usuário em ambos os casos:
<button id="copyBtn">Copy</button>
<span id="status"></span>
<script>
const button = document.getElementById('copyBtn');
const status = document.getElementById('status');
button.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText('Hello from W3docs!');
status.textContent = 'Copied!';
} catch (err) {
status.textContent = 'Copy failed';
console.error('Clipboard write failed:', err);
}
});
</script>O await pausa até que a gravação no clipboard seja concluída. Se o usuário tiver negado a permissão ou a página não estiver em um contexto seguro, a promise é rejeitada e o bloco catch é executado — por isso você nunca deve presumir que a cópia foi bem-sucedida.
Lendo Texto do Clipboard
Para ler o texto atual do clipboard, chame navigator.clipboard.readText(). Ele também retorna uma promise, desta vez resolvendo com o conteúdo de texto do clipboard:
<button id="pasteBtn">Read clipboard</button>
<p id="output"></p>
<script>
const button = document.getElementById('pasteBtn');
const output = document.getElementById('output');
button.addEventListener('click', async () => {
try {
const text = await navigator.clipboard.readText();
output.textContent = `Clipboard contains: ${text}`;
} catch (err) {
output.textContent = 'Could not read clipboard';
console.error('Clipboard read failed:', err);
}
});
</script>A leitura é muito mais sensível do que a gravação, pois expõe o que quer que o usuário tenha copiado — possivelmente uma senha ou outros dados privados. Por esse motivo, os navegadores protegem readText() de forma mais rigorosa: exige um gesto explícito do usuário, e alguns navegadores exibem um prompt de permissão na primeira vez, ou permitem leituras apenas quando a aba da página está em foco.
Requisitos e Armadilhas
A Clipboard API tem várias regras que, se ignoradas, levam a rejeições silenciosas. Tenha isso em mente sempre que a utilizar.
A Clipboard API só funciona em um contexto seguro — ou seja, HTTPS, ou localhost durante o desenvolvimento. Em uma página http:// simples, navigator.clipboard geralmente é undefined. As chamadas também geralmente exigem um gesto do usuário, como um clique ou pressionamento de tecla, portanto acioná-las no carregamento da página falhará. A Permissions API governa as permissões clipboard-read e clipboard-write, e leituras podem solicitar a confirmação do usuário. Como qualquer chamada pode ser rejeitada — permissão negada, documento sem foco ou navegador sem suporte — sempre envolva as chamadas de clipboard em try...catch.
A página também precisa estar em foco para muitas operações de clipboard. Se você chamar readText() a partir de, por exemplo, um setTimeout enquanto o usuário mudou para outra aba, espere uma rejeição com o erro "document is not focused". Observe também que uma leitura do clipboard só ocorre depois que o usuário tiver focado e interagido com sua página.
Copiando Dados Ricos com ClipboardItem
O texto é o caso simples. Para copiar dados que não sejam texto — imagens, HTML ou vários formatos ao mesmo tempo — use navigator.clipboard.write(), que recebe um array de objetos ClipboardItem. Cada ClipboardItem mapeia tipos MIME para seus dados (tipicamente um Blob).
O exemplo abaixo busca uma imagem, envolve o Blob resultante em um ClipboardItem e o copia:
async function copyImage(url) {
try {
const response = await fetch(url);
const blob = await response.blob();
const item = new ClipboardItem({ [blob.type]: blob });
await navigator.clipboard.write([item]);
console.log('Image copied to clipboard');
} catch (err) {
console.error('Failed to copy image:', err);
}
}A chave dentro do construtor ClipboardItem é o tipo MIME (aqui blob.type, por exemplo 'image/png'), e o valor são os dados para esse tipo. Um único item pode conter várias representações — por exemplo, tanto 'text/plain' quanto 'text/html' — permitindo que o aplicativo de destino escolha a melhor.
Ler dados ricos espelha isso com navigator.clipboard.read(), que resolve para um array de objetos ClipboardItem que você inspeciona por tipo:
async function readClipboardItems() {
const items = await navigator.clipboard.read();
for (const item of items) {
for (const type of item.types) {
const blob = await item.getType(type);
console.log(`Found ${type}`, blob);
}
}
}O suporte dos navegadores para os métodos de dados ricos (write e read com ClipboardItem) é mais restrito e menos consistente do que para os métodos de texto. Os tipos MIME de imagem, em particular, variam por navegador — image/png é o mais confiável. Faça detecção de funcionalidade com if ('write' in navigator.clipboard) e utilize cópia de texto ou URL como alternativa quando dados ricos não estiverem disponíveis.
O Fallback Legado
Antes da API assíncrona, copiar significava selecionar um elemento e chamar document.execCommand('copy'). Esse método agora está obsoleto, mas ainda funciona em navegadores mais antigos, sendo útil apenas como fallback. O padrão típico selecionava um <textarea> oculto e executava o comando:
function copyTextFallback(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy'); // deprecated
document.body.removeChild(textarea);
}O código moderno deve preferir a API assíncrona e usar o fallback apenas quando navigator.clipboard não estiver disponível:
async function copyText(text) {
if (navigator.clipboard) {
await navigator.clipboard.writeText(text);
} else {
copyTextFallback(text);
}
}Usos no Mundo Real
A Clipboard API impulsiona muitas interações pequenas, mas valiosas, que você vê todos os dias:
- Botões "Copiar código" em páginas de documentação, para que os leitores possam pegar um trecho sem precisar selecioná-lo manualmente.
- Botões "Copiar link para compartilhar" que colocam uma URL no clipboard para colar em chats ou e-mails.
- Cópia de saída gerada, como uma senha, uma chave de API ou uma citação formatada.
Seja o que for que você copie, forneça feedback visível. Um botão que copia silenciosamente deixa os usuários em dúvida sobre se funcionou. Troque o rótulo para "Copiado!", exiba um toast breve ou atualize um elemento de status adjacente — isso também é uma melhoria de acessibilidade, já que usuários de leitores de tela não recebem nenhuma indicação do clipboard pelo próprio navegador.