En varios formularios de registro he visto el mismo error disfrazado de buena intencion: apenas la persona termina de escribir su correo, la UI bloquea el boton principal mientras hace una comprobacion extra. A veces consulta dominios, a veces revisa si el email ya existe, y a veces intenta detectar un alias temporal. La validacion puede ser util, pero la forma de mostrarla suele castigar la experiencia mas de la cuenta.
Mi regla actual en React es simple: el submit no deberia desaparecer ni parecer roto solo porque hay una comprobacion asincrona en curso. El usuario necesita entender qué esta pasando, qué puede hacer ahora y qué pasará despues. Suena obvio, pero no siempre lo es en el codigo del dia a dia.
El problema real no es validar, es interrumpir
Cuando una pantalla deshabilita el CTA demasiado pronto, aparecen tres costes:
- el usuario cree que hizo algo mal aunque no haya error
- lector de pantalla recibe cambios pobres o tardios
- cada pulsacion dispara trabajo innecesario y ensucia el render
En productos con growth loops o registro social, ese microcorte se nota bastante. Nielsen Norman Group lleva años remarcando que el estado del sistema debe ser visible, y en formularios eso incluye explicar si una revision sigue corriendo o si ya termino. No basta con poner un spinner pequeñito y esperar que todos lo interpreten igual.
Tambien conviene recordar que no toda comprobacion merece frenar el flujo. Si tu equipo usa un generador de correo temporal para QA o para aislar escenarios de soporte, la interfaz no deberia tratar todos esos casos como una crisis. Incluso cuando alguien escribe algo que en notas internas llamarían tem email, el primer trabajo de la UI es aclarar, no castigar.
Que deberia pasar mientras React comprueba el email
Yo intento separar tres capas:
- validacion sintactica inmediata
- comprobacion asincrona no bloqueante
- decision final al enviar
La primera se resuelve en el cliente y debe ser rapida. La segunda puede consultar backend, pero sin romper el foco ni esconder el boton. La tercera ocurre en submit, que sigue siendo la fuente real de verdad.
Ese orden reduce ruido y mejora conversion porque el usuario mantiene el control. Es parecido a aislar correos de prueba para Facebook: separar señales ayuda a interpretar mejor el contexto. En frontend, separar estados hace lo mismo.
Un patron pequeno con estado derivado
Este patron me ha funcionado bien en formularios de React con chequeos de disponibilidad o politicas de dominio:
type EmailCheckState =
| { kind: "idle" }
| { kind: "checking" }
| { kind: "ok" }
| { kind: "warning"; message: string };
function EmailField() {
const [email, setEmail] = useState("");
const [checkState, setCheckState] = useState<EmailCheckState>({ kind: "idle" });
useEffect(() => {
if (!email.includes("@")) {
setCheckState({ kind: "idle" });
return;
}
const controller = new AbortController();
setCheckState({ kind: "checking" });
const timer = setTimeout(async () => {
const response = await fetch(`/api/email-check?value=${encodeURIComponent(email)}`, {
signal: controller.signal,
});
const result = await response.json();
setCheckState(result.ok ? { kind: "ok" } : { kind: "warning", message: result.message });
}, 250);
return () => {
controller.abort();
clearTimeout(timer);
};
}, [email]);
const canSubmit = email.length > 0;
return (
<>
<input
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
aria-describedby="email-help"
/>
<p id="email-help" aria-live="polite">
{checkState.kind === "checking" && "Comprobando el correo..."}
{checkState.kind === "warning" && checkState.message}
</p>
<button disabled={!canSubmit}>Crear cuenta</button>
</>
);
}
Lo importante no es el snippet exacto. Lo importante es que canSubmit no dependa de cada estado auxiliar. Si atas el boton a cualquier warning temporal, tu UI empieza a mandar señales mezcladas. Y luego todo se siente medio fragil, medio nervioso.
Detalles de accesibilidad que evitan confusion
Hay cuatro detalles chicos que pagan muy bien:
- usar
aria-live="polite"para mensajes no criticos - no mover el layout de forma brusca cuando aparece ayuda
- mantener el foco en el input hasta que la persona decida avanzar
- reservar el estado deshabilitado para errores reales o campos vacios
WebAIM suele insistir en que los mensajes de error deben ser especificos y programaticamente asociados al campo. Estoy de acuerdo, pero además intentaria distinguir warning de bloqueo. Un warning como "ese dominio suele fallar en correos de activacion" puede convivir con el submit. Un bloqueo real como "falta el simbolo @" no.
Si detrás hay APIs de registro o colas de correo, también ayuda conversar mejor con backend. En ese punto, tener logs utiles para colas de email hace que soporte y frontend vean la misma pelicula, no dos versiones parciales.
Checklist para no romper conversion
Antes de cerrar un formulario de este tipo, yo reviso esto:
- el boton principal sigue visible durante la comprobacion
- el texto de ayuda explica estado y siguiente paso
- los checks se cancelan al cambiar el valor
- el layout no salta cuando aparece feedback
- submit decide con datos finales, no con miedos temporales
No es una receta magica, pero suele dar formularios mas calmados y mas legibles. Y eso, curiosamente, tambien mejora rendimiento porque dejas de disparar renders y bloqueos que nadie pidio. Aveces el mejor polish no es agregar mas logica, sino quitar friccion.
Q&A
¿Cuando si bloquearias el submit?
Cuando el campo esta vacio, el formato es claramente invalido o la accion seria insegura sin correccion. No por un check auxiliar todavia en progreso.
¿Esto aplica solo a registros?
No. Tambien sirve en recovery, invitaciones y cambios de email donde hay revisiones de dominio o disponibilidad.
¿Y el caso de correo temporal para Facebook?
Lo trataria como contexto de producto o riesgo, no como razon automatica para esconder el CTA. Mejor advertir bien que reaccionar de forma brusca.
Top comments (0)