Botón de copiar al portapapeles (JavaScript y React)
Botón de copiar al portapapeles
Botón de copiar al portapapeles en JavaScript y React | We.Inc
Crea un botón de copiar con navigator.clipboard.writeText: demo funcional, un hook de React, copia de HTML enriquecido e imágenes con ClipboardItem, aviso accesible de Copiado y errores.
Ejecuta navigator.clipboard.writeText(text) desde un handler de clic. Devuelve una promesa, así que espérala con await, muestra un breve mensaje de Copiado y captura el caso de error. La API solo funciona en HTTPS o en localhost, y necesita una acción real del usuario como un clic, así que no la ejecutes al cargar la página.
Formas de poner datos en el portapapeles
Método
Copia
Estado
navigator.clipboard.writeText()
Texto plano
Baseline, ampliamente disponible desde marzo de 2020
Un botón de copiar parece trivial, pero los fragmentos que mejor posicionan suelen saltarse lo que falla: la promesa puede rechazarse, los marcos incrustados pueden estar bloqueados y los usuarios de lectores de pantalla nunca se enteran de que ha pasado algo. La demo de abajo copia de verdad en tu navegador, y el código cubre cada uno de esos casos.
Pulsa un botón Copiar en la demo. La etiqueta cambia a Copiado durante dos segundos y un mensaje de estado oculto lo anuncia.
Pega en el cuadro de prueba para comprobar exactamente qué ha llegado a tu portapapeles.
Prueba el botón de copia enriquecida y luego pega en un correo o un editor de documentos para ver cómo la versión HTML conserva el formato.
Copia abajo el código de JavaScript puro, el hook de React o el de copia enriquecida.
Gestiona siempre el rechazo. Muestra algo como Pulsa Ctrl+C para copiar y selecciona el texto para que la persona pueda terminar a mano.
Anuncia el éxito con una región role="status". Cambiar solo un icono no les dice nada a los usuarios de lectores de pantalla.
Mantén estable el ancho de la etiqueta del botón cuando cambie a Copiado, para que nada a su alrededor salte.
Copia el valor desde tus datos, no desde el innerText de un elemento con estilos, que puede incluir espacios ocultos o de más.
Dentro de un iframe, Chromium necesita que el padre permita clipboard-write mediante Permissions Policy, por ejemplo allow="clipboard-write".
Preguntas frecuentes
¿Por qué navigator.clipboard es undefined?
La página no está en un contexto seguro. La Clipboard API solo está disponible en páginas HTTPS y en localhost, así que un sitio servido por HTTP simple o desde una dirección de red local no la tendrá.
¿Por qué writeText lanza NotAllowedError?
El navegador rechazó la escritura, normalmente porque no hubo una acción reciente del usuario, la página no tenía el foco o Permissions Policy bloqueó un marco. Ejecútalo directamente dentro de un handler de clic y evita esperas largas con await antes.
¿Necesito una librería para copiar al portapapeles en React?
No. Un hook de diez líneas alrededor de navigator.clipboard.writeText es suficiente. Las librerías añaden sobre todo una alternativa con execCommand para navegadores muy antiguos, que los navegadores actuales no necesitan.
¿Puedo copiar una imagen al portapapeles?
Sí, con navigator.clipboard.write y un ClipboardItem cuya clave sea image/png y cuyo valor sea un Blob PNG. Usa ClipboardItem.supports() para comprobar antes un tipo; PNG es el formato que los navegadores aceptan de forma más fiable.
¿Sigue siendo válido document.execCommand('copy')?
Está obsoleto. Todavía funciona en muchos navegadores bajo condiciones variables, pero el código nuevo debería usar la Clipboard API asíncrona y, como alternativa, pedir a la persona que copie manualmente.
Las tres reglas que impone el navegador
Escribir en el portapapeles es una acción privilegiada, así que los navegadores exigen una página segura, un documento con el foco y, en Firefox y Safari, un gesto reciente del usuario para cada escritura. Chromium también puede conceder un permiso persistente de clipboard-write.
En la práctica, lanza la copia desde un clic o una pulsación de tecla, haz la escritura al principio del handler y trata el fallo como algo normal, no excepcional.
Un aviso que la gente pueda percibir
Cambia la etiqueta a Copiado durante unos dos segundos y luego restáurala. Un tooltip que solo aparece al pasar el ratón es invisible en pantallas táctiles y para los usuarios de teclado.
Pon el mismo mensaje en una región live con prioridad polite. role="status" lo anuncia sin interrumpir lo que el lector de pantalla estuviera leyendo.
Copias enriquecidas y eventos copy personalizados
Un ClipboardItem puede contener a la vez una versión text/plain y otra text/html. Las apps que entienden HTML pegan la versión con formato, y los campos de texto plano toman la otra.
Para cambiar lo que copia un Ctrl+C normal, como añadir una línea con la fuente, escucha el evento copy, ejecuta event.preventDefault() y define tus propios datos con event.clipboardData.setData().
JavaScript puro
Botón de copiar con aviso y alternativa
El elemento de estado está oculto visualmente pero se lee en voz alta. Si falla, el texto queda seleccionado para que la persona pueda pulsar Ctrl+C o Cmd+C.
Hook de React
useCopyToClipboard
Devuelve el estado y una función de copia. El temporizador se limpia al desmontar, así que nunca actualiza un componente eliminado.
Texto enriquecido, imágenes y el evento copy
Copiar HTML con alternativa en texto plano
Cada tipo MIME es un Blob. El destino donde se pega elige el formato más rico que entiende.
Añadir una línea con la fuente a las copias normales
Úsalo con moderación. Cambiar lo que la gente copia puede molestar, así que limítalo a citas o código donde la fuente aporte algo.
Errores y sus causas
Cuando la copia falla
Lo que ves
Causa probable
Solución
navigator.clipboard es undefined
Página servida por HTTP simple
Sirve por HTTPS, o prueba en localhost
NotAllowedError
Sin gesto del usuario, o la pestaña no tiene el foco
Ejecútalo dentro del handler de clic
NotAllowedError dentro de un iframe
Permissions Policy bloquea clipboard-write
Añade allow="clipboard-write" al iframe
Copia de imagen rechazada
Tipo MIME no compatible
Convierte a PNG y comprueba ClipboardItem.supports()
Copiado
Error
Copiar
Copiar texto enriquecido
Copiado al portapapeles
No se pudo copiar. Selecciona el texto y pulsa Ctrl+C o Cmd+C.
Pruébalo
Botones de copiar para texto plano y HTML enriquecido, con un cuadro para pegar y comprobar el resultado.