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.
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.