Publicado: 3 de julio de 2026
Si alguna vez usaste un editor de documentos, una app de hojas de cálculo o cualquier app en la que pegar contenido del portapapeles sea una interacción principal, sabes cuál es la expectativa del usuario: pegar debe ser instantáneo.
Lamentablemente, en muchos casos, las apps web leen más datos del portapapeles de los que necesitan, lo que puede hacer que pegar contenido se sienta más lento de lo que debería, desperdicia ciclos de CPU y usa memoria para datos que la app nunca necesita.
La lectura de formato selectiva aborda este problema de rendimiento modificando la API de Async Clipboard para satisfacer mejor las expectativas de los usuarios y proporcionar lo que necesitan los desarrolladores: leer solo los formatos que la app realmente necesita.
El problema: Leer todos los formatos de datos por adelantado
Cuando un usuario copia contenido de cualquier app (por ejemplo, Excel, Documentos de Google o Photoshop), la app suele escribir varias representaciones de los mismos datos en el portapapeles del sistema: texto sin formato, HTML, imagen y, a veces, formatos personalizados. La app web que lee los datos (cuando el usuario pega desde el portapapeles) rara vez necesita todas estas representaciones. Un editor de hojas de cálculo podría querer HTML. Un editor de texto podría querer exponer un comando "pegar como texto sin formato". Un editor de código solo podría querer el texto y nada más.
Hasta ahora, navigator.clipboard.read() recuperaba cada representación disponible de los datos del portapapeles del sistema, ejecutaba la limpieza (que puede llevar tiempo, especialmente con cargas útiles grandes de HTML o imágenes) y conservaba los bytes en la memoria, incluso para los formatos que la app nunca usaría. Cuanto más grande es la carga útil no utilizada, más tiempo debe esperar el usuario y más memoria consume la pestaña, justo cuando la capacidad de respuesta de pegado es más importante.
La solución: Solo enumera y, luego, lee a pedido
A partir de Chrome y Edge 149, los objetos ClipboardItem que devuelve el método navigator.clipboard.read() solo describen lo que hay en el portapapeles, en lugar de dar acceso a los datos de inmediato. Los bytes reales para un formato determinado se leen desde el portapapeles solo cuando llamas a ClipboardItem.getType(mimeType).
La superficie de la API no se modificó. El mismo código que escribiste ayer sigue funcionando hoy, lo que te brinda beneficios de rendimiento inmediatos:
// Enumerate the available MIME types.
// No payload is fetched yet.
const items = await navigator.clipboard.read();
for (const item of items) {
if (item.types.includes('text/html')) {
// Only read the HTML data.
// The data is fetched (and sanitized) here only.
const blob = await item.getType('text/html');
useThePastedHtml(await blob.text());
break;
}
// text/plain, image/png, or any other custom formats
// stored in the clipboard are never read.
}
Otro beneficio de rendimiento aquí es que el almacenamiento en caché es automático. Llamar a getType('text/html') dos veces en el mismo ClipboardItem lee desde el portapapeles del sistema solo una vez y devuelve el Blob almacenado en caché en la segunda llamada.
Los beneficios
Latencia. El trabajo que debía realizarse cuando se llamaba a navigator.clipboard.read(), como copiar bytes entre procesos, limpiar HTML o decodificar imágenes, ahora solo se realiza para los formatos que tu app realmente necesita. Las apps que solo leen un formato obtienen la mayor parte del ahorro. Cuanto más grandes sean los formatos que omitas, mayor será la ganancia. El HTML se beneficia más porque el saneamiento de HTML es costoso.
Memoria: Anteriormente, llamar a navigator.clipboard.read() cuando el portapapeles contenía una imagen de 50 MB significaba que se debían usar 50 MB en la memoria del renderizador de la página web hasta que se liberara ClipboardItem, incluso si la app solo necesitaba la representación text/plain de los datos. Ahora, solo los formatos que lee tu app cuando llama a ClipboardItem.getType() usan memoria en el proceso del renderizador. En el caso de las apps en las que es común pegar contenido, como los editores de documentos, los redactores de correos electrónicos o las herramientas de diseño, esto reduce significativamente el uso de memoria durante el pegado, en especial en las sesiones con varias pestañas en las que varios editores pueden leer el portapapeles en paralelo.
No se requiere habilitación. Todos los sitios web obtienen estos beneficios automáticamente. No es necesario migrar código ni restringir el acceso al código con lógica de detección de funciones.
Un cambio de comportamiento que debes tener en cuenta
Leer los datos más tarde introduce un ligero cambio en el comportamiento: un objeto ClipboardItem ya no es una instantánea inmutable del portapapeles en el momento en que se resuelve la llamada a navigator.clipboard.read().
Cuando se llama a read() y se muestra un ClipboardItem, este elemento es solo un controlador diferido de los datos en el portapapeles del sistema.
Si el contenido del portapapeles cambia entre la llamada a read() y una llamada posterior a getType(), esa llamada se rechazará con un InvalidStateError. Esto es así incluso para un formato que ya leíste correctamente en el mismo ClipboardItem.
// Read from the clipboard.
const items = await navigator.clipboard.read();
// Get the first ClipboardItem.
const item = items[0];
// Actually fetch the data now, which works fine.
const text = await item.getType('text/plain');
// The user writes something new to the clipboard
// by copying from another app, or your own code writes to
// the clipboard again.
await navigator.clipboard.writeText('something new');
// Fetching the data from the earlier ClipboardItem
// rejects with InvalidStateError, because the contents
// of the clipboard changed.
const html = await item.getType('text/html');
Esto sucede porque devolver datos que ya no reflejan el portapapeles sería confuso y, de lo contrario, generaría problemas difíciles de depurar.
En la práctica, el cambio es invisible para la mayoría del código, ya que las apps suelen llamar a read() y getType() de forma consecutiva dentro del mismo controlador de pegado. Sin embargo, es posible que desees observar los casos en los que tienes código que mantiene un ClipboardItem a través de límites asíncronos.
Este es un ejemplo en el que este cambio podría convertirse en un problema: almacenar en caché objetos ClipboardItem en window, mostrarlos en una IU del historial del portapapeles o esperar la interacción del usuario antes de llamar a getType(). Para evitar problemas con una situación como esta, haz lo siguiente:
- Encapsula las llamadas a
getType()en bloquestry / catchy trataInvalidStateErrorcomo un indicador de que el portapapeles se movió y el código debe volver a leerse. - Si necesitas conservar los datos del portapapeles, copia el objeto
Bloben tu propia estructura tan pronto como logetType(). No trates elClipboardItemcomo almacenamiento a largo plazo. - Usa el evento
clipboardChangepara invalidar de forma proactiva los identificadores obsoletos.
Estándares y compatibilidad con navegadores
La lectura de formato selectivo es un cambio en la especificación de las APIs de Clipboard impulsado por el Grupo de trabajo de edición web y el equipo de la plataforma web de Microsoft Edge.
Safari también admite este comportamiento, y Firefox indicó una posición positiva en cuanto a los estándares y la intención de alinearse.
Se trata de una mejora silenciosa, pero significativa, en el rendimiento y la interoperabilidad de la API de Clipboard, con la que Chromium ahora converge en el mismo modelo.