Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
import { useState, useCallback, useRef } from "react";
interface UseCopyToClipboardReturn {
/** O texto copiado mais recentemente, ou null */
copiedText: string | null;
/** Se o texto foi copiado recentemente (reseta após timeout) */
isCopied: boolean;
/** Copie o texto fornecido para a área de transferência */
copy: (text: string) => Promise<boolean>;
/** Redefina o estado copiado manualmente */
reset: () => void;
}
function useCopyToCopyToClipboard(
resetDelay: number = 2000
): UseCopyToClipboardReturn {
const [copiedText, setCopiedText] = useState<string | null>(null);
const [isCopied, setIsCopied] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const reset = useCallback(() => {
setCopiedText(null);
setIsCopied(false);
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
}, []);
const copy = useCallback(
async (text: string): Promise<boolean> => {
// Tenta a API Clipboard moderna primeiro
if (navigator?.clipboard?.writeText) {
try {
await navigator.clipboard.writeText(text);
setCopiedText(text);
setIsCopied(true);
// Redefine automaticamente após o atraso
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
setIsCopied(false);
timerRef.current = null;
}, resetDelay);
return true;
} catch {
// Falha na API Clipboard (por exemplo, permissão negada)
}
}
// Fallback: execCommand para navegadores mais antigos
try {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.left = "-9999px";
textarea.style.top = "-9999px";
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
const success = document.execCommand("copy");
document.body.removeChild(textarea);
if (success) {
setCopiedText(text);
setIsCopied(true);
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
setIsCopied(false);
timerRef.current = null;
}, resetDelay);
}
return success;
} catch {
return false;
}
},
[resetDelay]
);
return { copiedText, isCopied, copy, reset };
}Quando usar isso: Você tem um botão "Copiar" ao lado de trechos de código, chaves de API, URLs ou links de compartilhamento e deseja feedback visual quando a cópia for bem-sucedida.
"use client";
function CodeBlock({ code }: { code: string }) {
const { isCopied, copy } = useCopyToClipboard(3000);
return (
<div style={{ position: "relative", background: "#1e1e1e", padding: 16, borderRadius: 8 }}>
<pre style={{ color: "#d4d4d4", margin: 0 }}>
<code>{code}</code>
</pre>
<button
onClick={() => copy(code)}
style={{
position: "absolute",
top: 8,
right: 8,
padding: "4px 12px",
background: isCopied ? "#22c55e" : "#3b82f6",
color: "#fff",
border: "none",
borderRadius: 4,
cursor: "pointer",
transition: "background 0.2s",
}}
>
{isCopied ? "Copied!" : "Copy"}
</button>
</div>
);
}
function ShareLink({ url }: { url: string }) {
const { isCopied, copy } = useCopyToClipboard();
return (
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
<input value={url} readOnly style={{ flex: 1, padding: 8 }} />
<button onClick={() => copy(url)}>
{isCopied ? "Link copied!" : "Share"}
</button>
</div>
);
}O que isso demonstra:
isCopied é redefinido automaticamente após 3 segundos (ou os 2 segundos padrão)copy retorna um booleano para verificações programáticas de sucessodocument.execCommand para navegadores sem a API Clipboardnavigator.clipboard.writeText é a API moderna baseada em promessas. Ela requer um contexto seguro (HTTPS) e pode solicitar permissão.textarea fora da tela, seleciona seu conteúdo e executa o comando copy. Isso é depreciado, mas amplamente suportado.isCopied muda para true e é redefinido automaticamente após resetDelay ms. Isso aciona o feedback "Copied!" sem limpeza manual.| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
resetDelay | number | 2000 | Milissegundos antes de isCopied ser redefinido para false |
| Retorno | Tipo | Descrição |
|---|---|---|
copiedText | string ou null | O último texto copiado com sucesso |
isCopied | boolean | Se uma cópia recente foi bem-sucedida (redefine automaticamente) |
copy | (text: string) => Promise<boolean> | Dispara a cópia, retorna sucesso |
reset | () => void | Redefine o estado manualmente |
Copiar texto rico (HTML): Use navigator.clipboard.write com um ClipboardItem para conteúdo formatado:
const blob = new Blob([htmlString], { type: "text/html" });
const item = new ClipboardItem({ "text/html": blob });
await navigator.clipboard.write([item]);Copiar de um elemento: Aceite um ref em vez de uma string e leia innerText:
const copyFromRef = async (ref: React.RefObject<HTMLElement>) => {
const text = ref.current?.innerText ?? "";
return copy(text);
};copy retorna Promise<boolean> para que os chamadores possam await e reagir a falhas.copy de um manipulador de eventos, não de um timer ou efeito.false.document.execCommand("copy") é depreciado e pode ser removido. Correção: A API Clipboard é o caminho principal; o fallback é uma rede de segurança para navegadores legados.| Pacote | Nome do Hook | Notas |
|---|---|---|
usehooks-ts | useCopyToClipboard | API semelhante, sem fallback |
@uidotdev/usehooks | useCopyToClipboard | Mínimo, apenas API Clipboard |
react-use | useCopyToClipboard | Inclui estado de erro |
copy-to-clipboard (npm) | copy() | Não é um hook; função utilitária com fallback |
navigator.clipboard.writeText é a API moderna baseada em promessas e é a abordagem preferida.document.execCommand("copy") é depreciado, mas tem suporte mais amplo em navegadores mais antigos.isCopied muda para true imediatamente após uma cópia bem-sucedida.false após resetDelay milissegundos (padrão: 2000).Sim. copy retorna Promise<boolean>:
const success = await copy("algum texto");
if (!success) {
showErrorToast("Falha ao copiar");
}reset() limpa manualmente copiedText, define isCopied como false e cancela qualquer timer de redefinição automática pendente. Use-a quando precisar redefinir o estado antes que a redefinição automática ocorra (por exemplo, ao fechar um modal).
A API Clipboard requer um contexto seguro (HTTPS ou localhost). Em HTTP puro, navigator.clipboard é indefinido. O hook usa fallback para execCommand, mas teste ambos os caminhos para ter certeza.
Navegadores exigem que o acesso à área de transferência ocorra em resposta a um gesto do usuário (clique, pressionamento de tecla). Chamar copy() de um timer, efeito ou callback assíncrono sem uma ação do usuário anterior será bloqueado. Sempre acione-o de um manipulador de eventos.
<textarea> fora da tela é criado e anexado ao DOM.select() e execCommand("copy") são chamados.Use navigator.clipboard.write com um ClipboardItem:
const blob = new Blob([htmlString], { type: "text/html" });
const item = new ClipboardItem({ "text/html": blob });
await navigator.clipboard.write([item]);A interface nomeada UseCopyToClipboardReturn fornece documentação clara, autocompletar IDE e pode ser reutilizada se os consumidores precisarem tipar props que aceitam o valor de retorno do hook.
copy é tipado como (text: string) => Promise<boolean>. A entrada é sempre uma string e o retorno indica sucesso ou falha. Nenhum genérico é necessário.
Revisado por Chris St. John·Última atualização: 10 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥