Eventos da Área de Transferência
Intercepte operações de copiar, recortar e colar para um comportamento personalizado da área de transferência.
Busque em todas as páginas da documentação
Intercepte operações de copiar, recortar e colar para um comportamento personalizado da área de transferência.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
| Evento | Dispara Quando | Borbulha | Cancelável | Propriedade Chave |
|---|---|---|---|---|
onCopy | Usuário copia a seleção (Ctrl+C / Cmd+C ou menu de contexto) | Sim | Sim | clipboardData: DataTransfer |
onCut | Usuário recorta a seleção (Ctrl+X / Cmd+X ou menu de contexto) | Sim | Sim | clipboardData: DataTransfer |
onPaste | Usuário cola conteúdo (Ctrl+V / Cmd+V ou menu de contexto) | Sim | Sim | clipboardData: DataTransfer |
Todos os eventos da área de transferência fornecem uma propriedade
clipboardDatado tipoDataTransferque permite ler ou escrever dados em vários formatos (text/plain, text/html, tipos MIME personalizados).
Cartão de receita de referência rápida - pronto para copiar e colar.
// Adiciona atribuição ao copiar texto
function AttributedContent({ children }: { children: React.ReactNode }) {
const handleCopy = (e: React.ClipboardEvent) => {
const selection = window.getSelection()?.toString() ?? "";
const attributed = `${selection}\n\n-- Fonte: mysite.com`;
e.clipboardData.setData("text/plain", attributed);
e.preventDefault(); // Necessário para usar dados personalizados da área de transferência
};
return <div onCopy={handleCopy}>{children}</div>;
}
// Sanitiza a entrada colada
function SanitizedInput() {
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
e.preventDefault();
const text = e.clipboardData.getData("text/plain");
const sanitized = text.replace(/[<>]/g, ""); // remove colchetes angulares
document.execCommand("insertText", false, sanitized);
};
return <input onPaste={handlePaste} placeholder="Cole aqui (sanitizado)" />;
}Quando usar isso: Você precisa adicionar atribuição a textos copiados, sanitizar entradas coladas, lidar com a colagem de imagens para upload ou implementar um botão de cópia personalizado.
// components/CopyableCodeBlock.tsx
"use client";
import { useState, useRef } from "react";
export function CopyableCodeBlock({ code }: { code: string }) {
const [copied, setCopied] = useState(false);
const preRef = useRef<HTMLPreElement>(null);
const handleCopyClick = async () => {
try {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch {
// Fallback para contextos inseguros
const textarea = document.createElement("textarea");
textarea.value = code;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
return (
<div className="relative group">
<pre
ref={preRef}
className="bg-gray-900 text-gray-100 p-4 rounded overflow-x-auto"
>
<code>{code}</code>
</pre>
<button
onClick={handleCopyClick}
className="absolute top-2 right-2 px-2 py-1 text-xs bg-gray-700 text-white rounded opacity-0 group-hover:opacity-100 transition-opacity"
>
{copied ? "Copiado!" : "Copiar"}
</button>
</div>
);
}// components/PasteUploadArea.tsx
"use client";
import { useState } from "react";
type PastedFile = {
name: string;
type: string;
size: number;
preview: string;
};
export function PasteUploadArea() {
const [files, setFiles] = useState<PastedFile[]>([]);
const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => {
const items = e.clipboardData.items;
const newFiles: PastedFile[] = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind === "file") {
const file = item.getAsFile();
if (!file) continue;
const preview = URL.createObjectURL(file);
newFiles.push({
name: file.name || `pasted-${Date.now()}.${file.type.split("/")[1]}`,
type: file.type,
size: file.size,
preview,
});
}
}
if (newFiles.length > 0) {
e.preventDefault();
setFiles((prev) => [...prev, ...newFiles]);
}
};
return (
<div
onPaste={handlePaste}
tabIndex={0}
className="border-2 border-dashed border-gray-400 rounded p-8 text-center focus:border-blue-500 focus:outline-none"
>
<p className="text-gray-600 mb-4">
Clique aqui e cole uma imagem (Ctrl+V / Cmd+V)
</p>
{files.length > 0 && (
<div className="grid grid-cols-3 gap-4 mt-4">
{files.map((f, i) => (
<div key={i} className="border rounded p-2">
{f.type.startsWith("image/") ? (
<img
src={f.preview}
alt={f.name}
className="w-full h-32 object-cover rounded"
/>
) : (
<p className="text-sm">{f.name}</p>
)}
<p className="text-xs text-gray-500 mt-1">
{f.type} ({(f.size / 1024).toFixed(1)} KB)
</p>
</div>
))}
</div>
)}
</div>
);
}O que isso demonstra:
navigator.clipboard.writeText para cópia assíncrona moderna com fallback para contextos insegurosclipboardData.itemstabIndex={0} para que ele possa receber foco e eventos da área de transferênciaClipboardEvent nativo. O React os normaliza entre navegadores.clipboardData é um objeto DataTransfer que fornece getData(format), setData(format, data) e items para acesso a arquivos.e.preventDefault() é necessário ao usar setData -- sem isso, o navegador substitui seus dados personalizados pelo comportamento padrão de cópia.<div> deve ter tabIndex para ser focável e receber eventos de colagem.Copiar texto puro para a área de transferência (programático, sem evento):
function CopyButton({ text }: { text: string }) {
const handleClick = async () => {
await navigator.clipboard.writeText(text);
};
return <button onClick={handleClick}>Copiar</button>;
}Manipulação de colagem de imagem com verificação de tipo:
function ImagePasteHandler() {
const handlePaste = (e: React.ClipboardEvent) => {
const items = Array.from(e.clipboardData.items);
const imageItem = items.find((item) => item.type.startsWith("image/"));
if (!imageItem) return;
e.preventDefault();
const file = imageItem.getAsFile();
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
const dataUrl = event.target?.result as string;
// Use dataUrl para pré-visualização ou upload
};
reader.readAsDataURL(file);
};
return <div onPaste={handlePaste} tabIndex={0}>Cole uma imagem aqui</div>;
}Formatação de cópia personalizada (HTML + texto puro):
function RichCopyTable({ rows }: { rows: string[][] }) {
const handleCopy = (e: React.ClipboardEvent) => {
e.preventDefault();
// Define as versões em texto puro e HTML
const plainText = rows.map((row) => row.join("\t")).join("\n");
const html = `<table>${rows
.map((row) => `<tr>${row.map((c) => `<td>${c}</td>`).join("")}</tr>`)
.join("")}</table>`;
e.clipboardData.setData("text/plain", plainText);
e.clipboardData.setData("text/html", html);
};
return (
<table onCopy={handleCopy}>
<tbody>
{rows.map((row, i) => (
<tr key={i}>
{row.map((cell, j) => (
<td key={j} className="border px-2 py-1">{cell}</td>
))}
</tr>
))}
</tbody>
</table>
);
}Prevenindo colagem em campos de confirmação:
function ConfirmEmailField() {
return (
<div>
<label>Email</label>
<input type="email" name="email" />
<label>Confirmar Email</label>
<input
type="email"
name="confirmEmail"
onPaste={(e) => {
e.preventDefault();
// Opcionalmente, exiba uma dica: "Por favor, digite seu e-mail novamente"
}}
/>
</div>
);
}Usando a API Clipboard (navigator.clipboard) para leitura:
function ClipboardReader() {
const [content, setContent] = useState("");
const readClipboard = async () => {
try {
// Requer gesto do usuário e permissões
const text = await navigator.clipboard.readText();
setContent(text);
} catch (err) {
console.error("Falha ao ler a área de transferência:", err);
}
};
return (
<div>
<button onClick={readClipboard}>Ler Área de Transferência</button>
{content && <pre className="mt-2 p-2 bg-gray-100 rounded">{content}</pre>}
</div>
);
}// O genérico ClipboardEvent especifica o elemento
const handleCopy = (e: React.ClipboardEvent<HTMLDivElement>) => {
e.clipboardData; // DataTransfer (sempre disponível em eventos da área de transferência)
e.currentTarget; // HTMLDivElement
};
// os métodos clipboardData são totalmente tipados
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
const text: string = e.clipboardData.getData("text/plain");
const html: string = e.clipboardData.getData("text/html");
// items é um DataTransferItemList
const items: DataTransferItemList = e.clipboardData.items;
// Iterando sobre os itens
for (let i = 0; i < items.length; i++) {
const item: DataTransferItem = items[i];
item.kind; // "string" | "file"
item.type; // string do tipo MIME
if (item.kind === "file") {
const file: File | null = item.getAsFile();
}
}
};
// Usando o tipo de manipulador abreviado
const onCut: React.ClipboardEventHandler<HTMLTextAreaElement> = (e) => {
// e é React.ClipboardEvent<HTMLTextAreaElement>
};
// Tipos da API navigator.clipboard (embutidos em lib.dom.d.ts)
async function writeToClipboard(text: string): Promise<void> {
await navigator.clipboard.writeText(text);
}
async function readFromClipboard(): Promise<string> {
return navigator.clipboard.readText();
}e.preventDefault() é necessário ao usar setData -- Se você chamar e.clipboardData.setData() sem e.preventDefault(), o navegador substitui seus dados personalizados pelo comportamento padrão de cópia. Correção: Sempre chame e.preventDefault() antes ou depois de setData.
clipboardData está disponível apenas durante o evento -- O objeto DataTransfer é limpo após o término do manipulador de eventos. Você não pode armazenar o evento e ler clipboardData de forma assíncrona. Correção: Extraia os dados de que você precisa de forma síncrona dentro do manipulador e armazene-os em uma variável ou estado.
navigator.clipboard requer HTTPS e ativação do usuário -- A API assíncrona da área de transferência só funciona em contextos seguros (HTTPS ou localhost) e requer um gesto recente do usuário (clique, pressionamento de tecla). Correção: Forneça um fallback usando document.execCommand("copy") para contextos HTTP e sempre chame os métodos da área de transferência a partir de manipuladores de eventos.
navigator.clipboard.readText() aciona um prompt de permissão do navegador -- Ao contrário da escrita, a leitura da área de transferência requer permissão explícita do usuário. Este prompt pode confundir os usuários. Correção: Use manipuladores de eventos onPaste para ler o conteúdo colado em vez de ler proativamente a área de transferência, a menos que sua UX exija especificamente.
Arquivos colados têm nomes vazios ou genéricos -- Quando um usuário cola uma captura de tela, file.name geralmente está vazio ou algo como image.png. Correção: Gere nomes significativos usando timestamps ou contexto, como pasted-${Date.now()}.png.
document.execCommand("copy") é obsoleto -- Embora ainda amplamente suportado, pode ser removido em navegadores futuros. Correção: Use navigator.clipboard.writeText() como o método principal com execCommand apenas como fallback.
Elementos não-input não podem receber eventos de colagem sem foco -- Um <div> não disparará onPaste a menos que esteja focado. Correção: Adicione tabIndex={0} ou tabIndex={-1} para tornar o contêiner focável e instrua os usuários a clicar/focar na área primeiro.
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
API navigator.clipboard | Você precisa de leitura/escrita programática sem um evento da área de transferência | Você está interceptando cópia/colagem iniciada pelo usuário |
document.execCommand("copy") | Você precisa de acesso à área de transferência em contextos inseguros (HTTP) | Você pode usar a API moderna da área de transferência |
Bibliotecas de terceiros (ex: clipboard.js) | Você precisa de suporte de área de transferência entre navegadores com código mínimo | A API nativa e os eventos React cobrem suas necessidades |
Detecção de Ctrl+C com onKeyDown | Você precisa saber quando o usuário tenta copiar sem interceptá-lo | Você precisa modificar o conteúdo da área de transferência |
| Arrastar e soltar | Os usuários precisam mover conteúdo entre áreas visualmente | Copiar/colar é o padrão de interação esperado |
onCopy dispara quando o usuário copia (Ctrl+C / Cmd+C ou menu de contexto)onCut dispara quando o usuário recorta (Ctrl+X / Cmd+X ou menu de contexto)onPaste dispara quando o usuário cola (Ctrl+V / Cmd+V ou menu de contexto)clipboardData do tipo DataTransferSem e.preventDefault(), o navegador substitui seus dados personalizados da área de transferência pelo comportamento padrão de cópia. Você deve chamá-lo antes ou depois de setData() para garantir que seus dados personalizados sejam preservados.
Adicione tabIndex={0} (ou tabIndex={-1} para foco programático apenas) ao elemento para torná-lo focável. Eventos da área de transferência só disparam em elementos focados ou seus ancestrais via bubbling.
const handlePaste = (e: React.ClipboardEvent) => {
const items = e.clipboardData.items;
for (let i = 0; i < items.length; i++) {
if (items[i].kind === "file") {
const file = items[i].getAsFile();
if (file) console.log(file.name, file.type);
}
}
};navigator.clipboard.writeText() é a API assíncrona moderna, requer HTTPS e um gesto do usuáriodocument.execCommand("copy") é obsoleto, mas funciona em contextos inseguros (HTTP)navigator.clipboard como principal, com execCommand como fallbackNão. O objeto DataTransfer é limpo após o término do manipulador de eventos. Se você armazenar o evento e tentar ler clipboardData mais tarde (por exemplo, em um setTimeout ou após await), os dados terão desaparecido. Extraia todos os dados necessários de forma síncrona dentro do manipulador.
const handleCopy = (e: React.ClipboardEvent) => {
e.preventDefault();
e.clipboardData.setData("text/plain", "versão em texto puro");
e.clipboardData.setData("text/html", "<b>versão HTML</b>");
};writeText() requer apenas um gesto recente do usuárioreadText() aciona um prompt de permissão do navegador porque a leitura da área de transferência é uma operação sensível à privacidadeonPaste para ler o conteúdo colado em vez de ler proativamente a área de transferênciaconst handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
e.preventDefault();
const text = e.clipboardData.getData("text/plain");
const sanitized = text.replace(/[<>]/g, "");
document.execCommand("insertText", false, sanitized);
};Quando um usuário cola uma captura de tela, o sistema operacional não fornece um nome de arquivo significativo. file.name geralmente está vazio ou é genérico. Gere seus próprios nomes usando timestamps ou contexto, como pasted-${Date.now()}.png.
// Tipo de evento completo
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
e.clipboardData; // DataTransfer
e.currentTarget; // HTMLInputElement
};
// Tipo de manipulador abreviado
const onCut: React.ClipboardEventHandler<HTMLInputElement> = (e) => {};const handlePaste = (e: React.ClipboardEvent) => {
const items: DataTransferItemList = e.clipboardData.items;
for (let i = 0; i < items.length; i++) {
const item: DataTransferItem = items[i];
item.kind; // "string" | "file"
item.type; // string do tipo MIME
if (item.kind === "file") {
const file: File | null = item.getAsFile();
}
}
};<input
type="email"
name="confirmEmail"
onPaste={(e) => e.preventDefault()}
/>Chame e.preventDefault() no manipulador onPaste para bloquear completamente a operação de colagem.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥