- Gallery
- Auth & Onboarding
- PasswordReset
New
PasswordReset
Password reset flow: email input, confirmation sent, new password with strength meter.
Auth & Onboardingpasswordresetsecurityauthform
Dependencies
shadcn/ui components needed:
npx shadcn@latest add lucide-reactHow to use this component
Copy the code below into your project. Make sure you have the required shadcn/ui dependencies installed. Then import and use the component in your pages or layouts.
Code
1"use client";23import { useState } from "react";4import { Lock, Mail, ArrowLeft, Check } from "lucide-react";56function getStrength(pw: string): { score: number; label: string; color: string } {7 let s = 0;8 if (pw.length >= 8) s++;9 if (/[A-Z]/.test(pw)) s++;10 if (/[0-9]/.test(pw)) s++;11 if (/[^A-Za-z0-9]/.test(pw)) s++;12 const labels = ["Weak", "Fair", "Good", "Strong"];13 const colors = ["bg-red-500", "bg-orange-500", "bg-yellow-500", "bg-emerald-500"];14 return { score: s, label: labels[Math.max(s - 1, 0)] ?? "Weak", color: colors[Math.max(s - 1, 0)] ?? "bg-red-500" };15}1617export default function PasswordReset() {18 const [step, setStep] = useState<"email" | "sent" | "reset">("email");19 const [email, setEmail] = useState("");20 const [password, setPassword] = useState("");21 const [confirm, setConfirm] = useState("");22 const strength = getStrength(password);2324 if (step === "sent") {25 return (26 <div className="mx-auto w-full max-w-md rounded-2xl border border-zinc-200 bg-white p-8 text-center shadow-xl dark:border-zinc-800 dark:bg-zinc-950">27 <div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-emerald-50 dark:bg-emerald-900/20">28 <Mail className="h-7 w-7 text-emerald-600 dark:text-emerald-400" />29 </div>30 <h2 className="mb-2 text-xl font-bold text-zinc-900 dark:text-zinc-50">Check your email</h2>31 <p className="mb-6 text-sm text-zinc-500 dark:text-zinc-400">We sent a reset link to <span className="font-semibold text-zinc-900 dark:text-zinc-100">{email}</span></p>32 <button onClick={() => setStep("reset")} className="mb-3 w-full rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white transition-all hover:bg-violet-500 active:scale-[0.98]">I have the code</button>33 <button onClick={() => setStep("email")} className="flex w-full items-center justify-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200"><ArrowLeft className="h-3.5 w-3.5" /> Try a different email</button>34 </div>35 );36 }3738 if (step === "reset") {39 return (40 <div className="mx-auto w-full max-w-md rounded-2xl border border-zinc-200 bg-white p-8 shadow-xl dark:border-zinc-800 dark:bg-zinc-950">41 <div className="mb-6 text-center">42 <div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-violet-500 to-indigo-600"><Lock className="h-6 w-6 text-white" /></div>43 <h2 className="text-xl font-bold text-zinc-900 dark:text-zinc-50">Create new password</h2>44 <p className="mt-1 text-sm text-zinc-500 dark:text-zinc-400">Must be at least 8 characters</p>45 </div>46 <form onSubmit={(e) => e.preventDefault()} className="space-y-4">47 <div>48 <label className="mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300">New password</label>49 <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} className="w-full rounded-lg border border-zinc-300 bg-transparent px-3.5 py-2.5 text-sm outline-none transition-colors focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20 dark:border-zinc-700 dark:text-zinc-100" />50 {password && (51 <div className="mt-2">52 <div className="mb-1 flex gap-1">{Array.from({ length: 4 }).map((_, i) => (<div key={i} className={`h-1.5 flex-1 rounded-full transition-colors ${i < strength.score ? strength.color : "bg-zinc-200 dark:bg-zinc-700"}`} />))}</div>53 <p className={`text-xs font-medium ${strength.score >= 3 ? "text-emerald-600" : strength.score >= 2 ? "text-yellow-600" : "text-red-600"}`}>{strength.label}</p>54 </div>55 )}56 </div>57 <div>58 <label className="mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Confirm password</label>59 <input type="password" value={confirm} onChange={(e) => setConfirm(e.target.value)} className="w-full rounded-lg border border-zinc-300 bg-transparent px-3.5 py-2.5 text-sm outline-none transition-colors focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20 dark:border-zinc-700 dark:text-zinc-100" />60 {confirm && password !== confirm && <p className="mt-1 text-xs text-red-500">Passwords do not match</p>}61 {confirm && password === confirm && <p className="mt-1 flex items-center gap-1 text-xs text-emerald-600"><Check className="h-3 w-3" /> Passwords match</p>}62 </div>63 <button type="submit" className="w-full rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white transition-all hover:bg-violet-500 active:scale-[0.98]">Reset password</button>64 </form>65 </div>66 );67 }6869 return (70 <div className="mx-auto w-full max-w-md rounded-2xl border border-zinc-200 bg-white p-8 shadow-xl dark:border-zinc-800 dark:bg-zinc-950">71 <div className="mb-6 text-center">72 <div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-violet-500 to-indigo-600"><Lock className="h-6 w-6 text-white" /></div>73 <h2 className="text-2xl font-bold tracking-tight text-zinc-900 dark:text-zinc-50">Forgot password?</h2>74 <p className="mt-1 text-sm text-zinc-500 dark:text-zinc-400">No worries, we'll send you reset instructions</p>75 </div>76 <form onSubmit={(e) => { e.preventDefault(); setStep("sent"); }} className="space-y-4">77 <div>78 <label className="mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Email</label>79 <input type="email" required placeholder="you@example.com" value={email} onChange={(e) => setEmail(e.target.value)} className="w-full rounded-lg border border-zinc-300 bg-transparent px-3.5 py-2.5 text-sm outline-none transition-colors placeholder:text-zinc-400 focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20 dark:border-zinc-700 dark:text-zinc-100" />80 </div>81 <button type="submit" className="w-full rounded-lg bg-gradient-to-r from-violet-600 to-indigo-600 px-4 py-2.5 text-sm font-semibold text-white shadow-md transition-all hover:from-violet-500 hover:to-indigo-500 active:scale-[0.98]">Send reset link</button>82 </form>83 <button className="mt-4 flex w-full items-center justify-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200"><ArrowLeft className="h-3.5 w-3.5" /> Back to sign in</button>84 </div>85 );86}