Un estado de carga parece un detalle pequeño, pero decide si una interfaz transmite control o incertidumbre. En React, una petición asíncrona puede tardar solo unos cientos de milisegundos y aun así dejar al usuario preguntándose si el botón funcionó.
La solución no es poner un spinner en cada sitio. Conviene diseñar una respuesta que informe, conserve el contexto visual y pueda ser entendida por un lector de pantalla. El resultado suele ser mejor rendimiento percibido sin trucos que oculten el estado real de la aplicación.
El estado de carga también es una interfaz
Hay tres preguntas que cada estado debería responder:
- ¿Qué acción está ocurriendo?
- ¿Qué parte de la pantalla cambiará?
- ¿Puede la persona seguir usando el resto de la página?
Un formulario de registro, por ejemplo, no necesita congelar toda la pantalla mientras espera una verificación de correo. Es suficiente con desactivar el botón que envió la petición, conservar su etiqueta y anunciar el resultado en una región viva.
Este enfoque también ayuda a observar el producto. En vez de tratar cada espera como un caso aislado, se pueden comparar señales con contexto antes de tocar producción y entender donde se pierde tiempo.
Un patrón pequeño para React
El componente no necesita una librería adicional para empezar. Un estado explícito y una región aria-live cubren una base útil:
function VerifyEmailButton({ onVerify, status }) {
const busy = status === "loading";
return (
<>
<button type="button" onClick={onVerify} disabled={busy} aria-busy={busy}>
{busy ? "Comprobando…" : "Verificar correo"}
</button>
<p role="status" aria-live="polite">
{status === "loading" && "Estamos comprobando el mensaje."}
{status === "success" && "La dirección fue verificada."}
{status === "error" && "No pudimos completar la comprobación."}
</p>
</>
);
}
La etiqueta del botón permanece estable: solo cambia cuando la acción está en progreso. disabled evita envíos repetidos y aria-busy comunica que el control está ocupado. La región de estado no debería recibir foco de forma automática, porque eso puede mover a la persona lejos de su posición actual.
En interfaces reales, el dato llega a veces tarde. Por eso es mejor representar loading, success y error como estados mutuamente claros, en lugar de inferirlos desde un texto vacío o desde la existencia de una respuesta.
CSS y accesibilidad deben ir juntos
Un skeleton puede mejorar la sensación de velocidad si mantiene la geometría del contenido final. Pero no debe parpadear indefinidamente ni convertirse en información duplicada para tecnologías de asistencia.
.loading-bar {
min-height: 2.75rem;
border-radius: 0.4rem;
background: #e7e9ed;
animation: pulse 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.loading-bar {
animation: none;
}
}
El contraste del estado terminado importa tanto como el del skeleton. También hay que comprobar el orden de lectura, el foco después de un error y el comportamiento con zoom. Un spinner que luce elegante pero no explica nada es solo decoración con retraso.
Para flujos largos, vale la pena medir el tiempo hasta la primera respuesta visual y el tiempo hasta el resultado útil. No son la misma métrica: una animación puede aparecer rápido mientras la persona sigue sin poder completar la tarea.
Cómo probarlo con una verificación de correo
Un caso de prueba sencillo es una pantalla que envía un enlace de verificación. Prueba al menos estas condiciones:
- La red responde rápido: el cambio de estado no debe producir un salto visible.
- La red tarda: el botón debe explicar que sigue trabajando y no permitir duplicados.
- La petición falla: el mensaje debe indicar el siguiente paso, no solo decir “Error”.
- El mensaje llega tarde o no llega: la interfaz debe ofrecer reintentar sin perder el correo escrito.
En pruebas de desarrollo puede usarse un generador de correo temporal para aislar el flujo, siempre dentro de un entorno permitido. La finalidad es revisar el recorrido, no llenar bandejas personales. Incluso conviene registrar el tiempo entre el envío y la confirmación; una prueba con tempail que simplemente “parece funcionar” no demuestra que el estado de la interfaz sea correcto.
Cuando el flujo forma parte de un onboarding, también es útil medir cada paso del onboarding y relacionarlo con el estado que vio la persona. Así se distingue un problema de entrega de correo de un problema de feedback en pantalla.
Preguntas frecuentes
¿Spinner o skeleton?
Usa skeleton cuando conoces la forma aproximada del contenido y quieres evitar un cambio grande de layout. Usa un mensaje breve o un spinner cuando la espera es corta y la forma final no se puede anticipar.
¿Debo mover el foco al resultado?
No siempre. Para un resultado pequeño junto al botón, una región role="status" suele ser suficiente. Mueve el foco solo cuando la nueva vista exige una decisión inmediata, y hazlo de manera consistente.
¿Un estado deshabilitado es suficiente?
No. Evita el doble envío, pero no explica la espera. Combínalo con texto visible y un anuncio accesible; la misma información debe estar disponible para todas las personas.
Checklist final
- Mantener el contexto visual mientras carga una petición.
- Evitar doble envío con estado explícito.
- Anunciar cambios con
aria-livesin robar el foco. - Respetar
prefers-reduced-motion. - Probar éxito, error, demora y reintento.
- Medir el resultado útil, no solo la aparición del spinner.
Un estado de carga bien diseñado no hace que la red sea mas rápida. Hace que la interfaz sea honesta, predecible y mas fácil de usar mientras la red termina su trabajo.
Top comments (0)