Caso de estudio · Código abierto
react-smart-copy
Una biblioteca de React headless y de código abierto para interacciones con el portapapeles. Modela toda la interacción alrededor de una copia (copiando, copiado, error, reintento y pegado) para que los equipos obtengan respuestas honestas, controles accesibles y errores tipados sin reescribir el mismo código frágil.
npm install react-smart-copyPrueba del proyecto
Mi rol
- Diseñé la API, el modelo de estados y el contrato de accesibilidad, y escribí la biblioteca en TypeScript estricto.
- Redacté la documentación, la matriz de lo que los navegadores pueden copiar y la demo.
- Publico y mantengo el paquete en npm bajo la licencia MIT.
Restricciones
- La biblioteca solo podía prometer lo que los navegadores admiten y debía reportar todo lo demás como un error tipado.
- Debía ser sin estilos y funcionar con Tailwind, CSS modules, CSS-in-JS y bibliotecas de componentes.
- Debía ser segura para renderizado en servidor, React StrictMode y React 18 y 19.
Decisiones clave
- Separé un núcleo independiente del framework de la capa de React para que la misma máquina de estados funcione en cualquier entorno.
- Separé el estado real del estado visible para evitar parpadeos y cambios de tamaño.
- Integré por defecto el acceso por teclado, nombres estables y anuncios en región en vivo.
Contexto
Los botones de copiar aparecen en casi todos los productos: claves de API, identificadores, correos, enlaces de invitación y ejemplos de código. Suelen escribirse como una sola llamada al portapapeles con un mensaje de "Copiado", y se reescriben en cada base de código.
Esa llamada oculta casi todo el comportamiento real. Los navegadores rechazan escrituras fuera de HTTPS, las bloquean si cambia el foco, solo admiten algunos formatos y reportan fallos de forma inconsistente. La interfaz debe explicar todo eso a quien pulsó el botón.
Problema
Las implementaciones habituales fallan de formas que el usuario no ve y que los equipos rara vez prueban. El objetivo era una biblioteca que trate la copia como comportamiento de producto con estados definidos, no como una función utilitaria.
- Fallos como un contexto inseguro o un permiso denegado suelen no hacer nada en silencio.
- Una copia tarda unos milisegundos, así que un texto de estado ingenuo muestra "Copiando…" durante un fotograma y cambia el tamaño del botón.
- Los botones ocultos con display: none al pasar el cursor no se alcanzan con el teclado.
- El resultado rara vez se anuncia a usuarios de lectores de pantalla.
- El renderizado en servidor puede no coincidir cuando un componente lee el portapapeles durante el render.
Enfoque
La biblioteca se construye por capas. Un núcleo independiente del framework contiene una máquina de estados de copia, validación de contenido, un adaptador del portapapeles del navegador y la clasificación de errores. La capa de React añade hooks y un componente compuesto CopyField sobre ese núcleo.
Cada estado es explícito y observable: idle, copying, copied y error. Dos estados separan la lógica de la presentación: el estado real cambia al instante para lógica y pruebas, mientras que el estado visible solo muestra "Copiando…" cuando la copia es lenta y lo mantiene lo suficiente para evitar parpadeos.
- Copia texto, HTML enriquecido con texto plano obligatorio, imágenes PNG, JSON y varios formatos a la vez.
- Pega texto, HTML y capturas con usePaste, y el pegado con teclado no pide permisos.
- CopyGroup coordina filas para que solo una muestre "Copiado" a la vez.
- Un punto de entrada de captura copia un SVG o una parte de la interfaz como PNG.
- Los adaptadores personalizados permiten que entornos como Electron aporten su propio portapapeles.
Decisiones de accesibilidad
Las reglas de accesibilidad forman parte de los componentes, no de una documentación que el equipo deba recordar. El control siempre es un botón real y su nombre accesible se mantiene estable mientras el resultado se anuncia por separado.
- Los controles se alcanzan con Tab y se activan con Enter o Espacio.
- Las acciones reveladas permanecen en el DOM y en el orden de tabulación; se ocultan con opacidad, nunca con display: none.
- El foco de teclado fija la aparición; un clic de ratón no.
- El botón nunca se deshabilita mientras copia, así el foco de teclado no se pierde.
- La región en vivo se monta antes de su primer mensaje, para no perder el primer anuncio.
- Las animaciones respetan prefers-reduced-motion.
Errores, SSR y tamaño
Cada fallo es un error tipado y clasificado, como unsupported, insecure-context, permission-denied, not-focused, unsupported-format o too-large, e indica si un reintento puede ayudar. Nada falla en silencio.
La entrada de React incluye "use client" y nunca accede a window durante el render, así que el servidor y el primer render del cliente coinciden en Next.js. El paquete no tiene dependencias en tiempo de ejecución, y el README indica unos 3,2 kB para useCopy y 5,4 kB para todas las funciones de copia, minificados y comprimidos con Brotli.
Resultados
- Publicado en npm como react-smart-copy bajo la licencia MIT, con documentación y demo en GitHub Pages.
- Copia texto, HTML enriquecido, PNG, JSON y contenido multiformato, y pega texto, HTML e imágenes.
- Sustituye los fallos silenciosos del portapapeles por errores tipados que indican si conviene reintentar.
- Funciona en este portfolio mediante su núcleo independiente del framework, en los botones de copiar, sin cargar React.
Próximos pasos
- Lee la documentación y prueba la demo.
- Instálalo con npm install react-smart-copy.
- Informa de problemas o contribuye en GitHub.