Files
skills/docs/ui/react/react-auth/src/components/email-verification-view/email-verification-view.md
2026-05-29 08:20:56 +01:00

4.8 KiB

EmailVerificationView

(no submodules)

Purpose

<EmailVerificationView> is the pure-UI surface that renders the four states of an email-verification flow — verifying, success, expired, error — with the right icon, copy, and CTA per state. It is a status view rather than a form: there are no inputs, no validation, just a deterministic switch from status to one of four cards. The page-level host owns the token-extraction, the API call against useAuth().verifyEmail, and the status transitions; the view is a dumb renderer that can also be reused inside admin re-verification flows or as a notification banner.

Features

  • Four discrete render branches keyed off the status prop, each with its own icon (Loader2 / CheckCircle2 / AlertTriangle / XCircle).
  • Each branch ships a CTA wired to one of onContinueToLogin / onRegisterAgain / onTryAgain callbacks supplied by the host.
  • The error branch additionally renders a destructive <Alert> with the same message for screen-reader prominence.
  • The error retry CTA shows a spinner and is disabled while isLoading is true.
  • Optional message prop overrides the default per-status description copy.
  • translations?: Partial<AuthEmailVerificationStrings> for full Welsh / zh-CN coverage via Wave 1's translation packs.
  • Always renders a "back to login" link except in the verifying state (where no action is meaningful yet).

Usage: Basic

import { EmailVerificationView } from "@suaveplan/react-auth";
import { BrowserRouter } from "react-router-dom";

export function VerifyEmailPage() {
    return (
        <BrowserRouter>
            <EmailVerificationView status="verifying" />
        </BrowserRouter>
    );
}

Usage: Advanced

Drive every transition from URL state + useAuth().verifyEmail:

import {
    EmailVerificationView,
    useAuth,
} from "@suaveplan/react-auth";
import {
    useEffect,
    useState,
} from "react";
import { useNavigate, useSearchParams } from "react-router-dom";

export function VerifyEmail() {
    const { verifyEmail } = useAuth();
    const navigate = useNavigate();
    const [params] = useSearchParams();
    const token = params.get("token");
    const [status, setStatus] = useState<
        "verifying" | "success" | "error" | "expired"
    >("verifying");
    const [message, setMessage] = useState("");

    useEffect(() => {
        if (token === null) {
            setStatus("expired");
            return;
        }
        verifyEmail({ token })
            .then(() => setStatus("success"))
            .catch((err: unknown) => {
                setStatus("error");
                setMessage(
                    err instanceof Error ? err.message : String(err)
                );
            });
    }, [token, verifyEmail]);

    return (
        <EmailVerificationView
            status={status}
            message={message}
            onContinueToLogin={() => navigate("/auth/login")}
            onRegisterAgain={() => navigate("/auth/register")}
            onTryAgain={() => setStatus("verifying")}
        />
    );
}

API Reference

EmailVerificationView(props: EmailVerificationViewProps): ReactElement

Prop Type Default Description
status "verifying" | "success" | "error" | "expired" — required Which card to render.
message string "" Override for the default description copy.
onContinueToLogin () => void — Fired by the success-status CTA.
onRegisterAgain () => void — Fired by the expired-status CTA.
onTryAgain () => void — Fired by the error-status retry CTA.
isLoading boolean false Disables the error-status retry CTA.
translations Partial<AuthEmailVerificationStrings> enGb.auth.emailVerification Override any subset of the 8 copy keys.

EmailVerificationStatus

type EmailVerificationStatus =
    | "verifying"
    | "success"
    | "error"
    | "expired";

Implementation Notes

  • Each card is implemented as a small internal component (VerifyingCard, SuccessCard, ExpiredCard, ErrorCard) and dispatched through a switch-style helper to keep the public file under the 300-line cap.
  • The component intentionally does NOT consume useTranslation — Wave 1 dropped the @suaveplan/react-translate dependency, and Wave 2 follows that precedent. Translations are injected via the translations prop instead.
  • The verifying card carries role="status" on the spinner wrapper for assistive-tech announcements.
  • The error card duplicates the message in a destructive <Alert> so the failure is announced even when the CTA is off-screen on narrow viewports.
  • No OTel instruments emitted from the view; metrics live on the Wave 1 verifyEmail action.