DEV Community

Silviu Technology
Silviu Technology

Posted on

React: confirma emails sin marear al usuario

Cuando una accion de email sale bien pero la UI se siente rara, el usuario no piensa "ah, interesante, esto es un problema de feedback". Piensa que algo fallo. En frontend pasa mucho con banners de confirmacion, botones que se desactivan sin contexto o mensajes que aparecen tarde y empujan medio layout. No parece grave, pero desgasta confianza bastante rapido.

En equipos de React he visto que el cuello no suele estar en el request. Suele estar en como contamos lo que acaba de pasar. Si tu flujo de "te enviamos un correo" cambia demasiado, vibra mucho o mueve el foco, la gente repite clicks, duda y abre tickets medio absurdos. Es un fallo pequeño, pero duele.

Por que el mensaje de confirmacion falla mas que la API

La mayoria de estos flujos tienen tres problemas repetidos:

  1. El mensaje aparece lejos del boton que disparo la accion.
  2. La confirmacion entra con animacion fuerte aunque el usuario solo queria una señal corta.
  3. El bloque ocupa altura distinta en cada estado y todo brinca un poco.

Eso afecta accesibilidad y tambien rendimiento percibido. Google lleva tiempo insistiendo en la estabilidad visual como parte de una buena experiencia, y el trabajo sobre Cumulative Layout Shift explica bastante bien por que esos movimientos inesperados pegan en usabilidad, no solo en una metrica.

En otras palabras: aunque el correo salga en 400 ms, la persona puede sentir que el flujo fue lento o inseguro si el interfaz se comporta raro. A veces hasta buscan deprisa algo como temp gamil com o cualquier bandeja de prueba que recuerden para verificar "a mano", porque la app no les transmitio calma. Es un sintoma UX bastante clasico.

Lo que cambio en mis revisiones de frontend

Hace un tiempo deje de revisar estos flujos solo como "estado loading + estado success". Ahora los miro como una secuencia de lectura:

  1. Que ve la persona justo despues del click.
  2. Que oye un lector de pantalla si el cambio importa.
  3. Que tanto se mueve el layout mientras espera.
  4. Que pista queda visible si el correo tarda un poco mas de la cuenta.

Eso me acerco a una regla simple: el feedback de confirmacion debe vivir donde nace la accion, reservar su espacio desde el inicio y usar movimiento minimo. No hace falta montar una mini fiesta visual para decir "correo enviado". De hecho, suele ser peor.

Cuando quiero comparar enfoques, me viene bien revisar casos cercanos como emails aislados por rama o esta guia sobre probar correos de onboarding sin ensuciar metricas. No son articulos de UI pura, pero recuerdan algo util: si no separas bien señal, contexto y prueba, todo se vuelve mas confuso de lo necesario.

Un patron pequeno en React que aguanta mejor

El patron que mas me convence tiene cuatro detalles:

  1. Mantener una zona fija para el mensaje.
  2. Usar role="status" con aria-live="polite".
  3. No desmontar el boton; solo cambiar su etiqueta o deshabilitarlo un momento.
  4. Respetar prefers-reduced-motion para que la confirmacion no entre haciendo demasiado ruido.
import { useId, useState } from "react";

type NoticeState = "idle" | "pending" | "sent" | "error";

export function EmailNotice() {
  const messageId = useId();
  const [state, setState] = useState<NoticeState>("idle");
  const [message, setMessage] = useState(" ");

  async function sendConfirmation() {
    setState("pending");
    setMessage("Enviando confirmacion...");

    try {
      const response = await fetch("/api/email/confirmation", {
        method: "POST"
      });

      if (!response.ok) throw new Error("send_failed");

      setState("sent");
      setMessage("Listo, te enviamos un correo de confirmacion.");
    } catch {
      setState("error");
      setMessage("No pudimos enviar el correo. Prueba otra vez.");
    }
  }

  return (
    <section aria-busy={state === "pending"}>
      <button
        type="button"
        disabled={state === "pending"}
        onClick={sendConfirmation}
        aria-describedby={messageId}
      >
        {state === "pending" ? "Enviando..." : "Enviar email"}
      </button>

      <p id={messageId} role="status" aria-live="polite">
        {message}
      </p>
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

No es un componente brillante, pero casi siempre mejora la lectura del flujo. La clave esta en no castigar al usuario con cambios bruscos cuando la tarea es sencilla. Tambien ayuda dejar una altura minima al mensaje con CSS para que el contenido no pegue saltitos algo molestos.

Si quieres ir un paso mas, puedes diferir animaciones decorativas y priorizar primero el texto. En este punto React y Rendimiento se cruzan bonito: menos trabajo visual innecesario suele dar una experiencia mas limpia y mas estable, incluso cuando el backend sigue siendo el mismo.

Como comprobar si el cambio de verdad ayuda

Yo suelo mirar cuatro señales:

  1. Menos dobles clicks sobre el mismo CTA.
  2. Menos abandonos justo despues de "enviar confirmacion".
  3. Menos tickets del tipo "no llego el email" cuando si habia llegado.
  4. Mejor tiempo hasta feedback visible en sesiones reales.

No hace falta instrumentar medio planeta. Con eventos pequeños como email_send_started, email_notice_shown y email_send_completed ya ves bastante. Si el correo tarda, vale. Lo importante es que la interfaz no parezca rota mientras espera. Es una diferencia sutil, pero muy real, y a veces se nota al toque en soporte.

Q&A

Debo usar un toast global para todo?

Yo no lo haria por defecto. Para confirmaciones de email prefiero feedback local, pegado al control. El toast global se pierde facil y, aveces, compite con otras alertas.

Tiene sentido animar el banner?

Si, pero poco. Un fade corto puede quedar bien. Un slide grande o rebote se siente excesivo para una accion tan comun.

Esto ayuda tambien en mobile?

Mucho. En pantallas pequeñas cualquier salto vertical se nota mas. Si reservas espacio y mantienes el foco donde toca, la experiencia queda bastante mas clara.

Top comments (0)