Skip to content
All Taste components

Auth

Sign-up with code

The second sign-up step: enter the six-digit code sent to your email.

step progress · 6-digit code · resend timer · change email

Step 2 of 3Change email

Check your email

We sent a 6-digit code to asha@saffronstudio.in. It expires in 10 minutes.

Verification code

No code? Resend in 0:42

"use client";
import { useEffect, useRef, useState } from "react";
import { Mail } from "lucide-react";

export function SignUpCodeStep({ email = "asha@saffronstudio.in", length = 6, onVerify, onResend, onChangeEmail }: {
  email?: string;
  length?: number;
  onVerify?: (code: string) => void;
  onResend?: () => void;
  onChangeEmail?: () => void;
}) {
  const [digits, setDigits] = useState<string[]>(Array(length).fill(""));
  const [wait, setWait] = useState(45);
  const refs = useRef<(HTMLInputElement | null)[]>([]);

  useEffect(() => {
    if (wait <= 0) return;
    const t = setTimeout(() => setWait((w) => w - 1), 1000);
    return () => clearTimeout(t);
  }, [wait]);

  const setAt = (i: number, v: string) => {
    const clean = v.replace(/[^0-9]/g, "");
    if (clean.length > 1) {
      // Pasted code: spread it across the boxes.
      const next = clean.slice(0, length).split("");
      setDigits(Array.from({ length }, (_, k) => next[k] ?? ""));
      refs.current[Math.min(next.length, length - 1)]?.focus();
      return;
    }
    setDigits((d) => d.map((x, k) => (k === i ? clean : x)));
    if (clean && i < length - 1) refs.current[i + 1]?.focus();
  };

  const code = digits.join("");
  const done = code.length === length;

  return (
    <div className="grid w-full max-w-[380px] gap-5 rounded-lg border border-border bg-bg p-7">
      <div className="grid gap-2">
        <div className="flex justify-between text-[12.5px]">
          <span className="text-fg-subtle">Step 2 of 3</span>
          <button type="button" onClick={onChangeEmail} className="text-primary hover:underline">Change email</button>
        </div>
        <div className="flex gap-1.5" aria-hidden>
          {[0, 1, 2].map((s) => <span key={s} className={"h-1 flex-1 rounded-full " + (s < 2 ? "bg-primary" : "bg-surface-2")} />)}
        </div>
      </div>

      <div className="grid gap-3">
        <span className="grid size-10 place-items-center rounded-full bg-primary-soft text-primary-soft-fg"><Mail className="size-[18px]" aria-hidden /></span>
        <div>
          <h2 className="text-xl font-medium tracking-tight">Check your email</h2>
          <p className="mt-1 text-[13.5px] text-fg-muted">
            We sent a {length}-digit code to <b className="font-medium text-fg">{email}</b>. It expires in 10 minutes.
          </p>
        </div>
      </div>

      <form onSubmit={(e) => { e.preventDefault(); if (done) onVerify?.(code); }} className="grid gap-5">
        <fieldset>
          <legend className="mb-1.5 text-[13px] font-medium">Verification code</legend>
          <div className="grid grid-cols-6 gap-2">
            {digits.map((d, i) => (
              <input key={i} ref={(el) => { refs.current[i] = el; }} value={d} inputMode="numeric"
                autoComplete={i === 0 ? "one-time-code" : "off"} aria-label={"Digit " + (i + 1)}
                onChange={(e) => setAt(i, e.target.value)}
                onKeyDown={(e) => { if (e.key === "Backspace" && !d && i > 0) refs.current[i - 1]?.focus(); }}
                className="h-12 w-full rounded border border-border-strong bg-bg text-center text-xl font-medium tabular-nums outline-none focus:border-primary focus:ring-[3px] focus:ring-primary/20" />
            ))}
          </div>
        </fieldset>
        <button type="submit" disabled={!done}
          className="h-9 rounded bg-primary text-sm font-medium text-primary-fg hover:bg-primary-hover disabled:opacity-50">
          Verify and continue
        </button>
      </form>

      <p className="text-center text-[13px] text-fg-subtle">
        No code?{" "}
        {wait > 0 ? (
          <span className="tabular-nums">Resend in 0:{String(wait).padStart(2, "0")}</span>
        ) : (
          <button type="button" onClick={() => { onResend?.(); setWait(45); }} className="text-primary hover:underline">Resend code</button>
        )}
      </p>
    </div>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.