New

SignupWizard

3-step signup wizard: email/password, profile info, preferences, with animated progress dots.

Auth & Onboardingsignupwizardmulti-steponboardingform

Dependencies

shadcn/ui components needed:

npx shadcn@latest add lucide-react

How 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";
2
3import { useState } from "react";
4import { ArrowRight, ArrowLeft, Check, User, Mail, Settings } from "lucide-react";
5
6export default function SignupWizard() {
7 const [step, setStep] = useState(0);
8 const [form, setForm] = useState({ email: "", password: "", name: "", bio: "", theme: "system", notifications: true });
9 const steps = [
10 { label: "Account", icon: Mail },
11 { label: "Profile", icon: User },
12 { label: "Preferences", icon: Settings },
13 ];
14
15 return (
16 <div className="mx-auto w-full max-w-lg rounded-2xl border border-zinc-200 bg-white p-8 shadow-xl dark:border-zinc-800 dark:bg-zinc-950">
17 {/* Progress dots */}
18 <div className="mb-8 flex items-center justify-center gap-2">
19 {steps.map((s, i) => (
20 <div key={s.label} className="flex items-center gap-2">
21 <div
22 className={`flex h-9 w-9 items-center justify-center rounded-full text-sm font-semibold transition-all duration-300 ${
23 i < step
24 ? "bg-emerald-500 text-white"
25 : i === step
26 ? "bg-violet-600 text-white shadow-lg shadow-violet-500/30"
27 : "bg-zinc-100 text-zinc-400 dark:bg-zinc-800 dark:text-zinc-500"
28 }`}
29 >
30 {i < step ? <Check className="h-4 w-4" /> : <s.icon className="h-4 w-4" />}
31 </div>
32 {i < steps.length - 1 && (
33 <div className={`h-0.5 w-8 rounded-full transition-colors duration-300 ${i < step ? "bg-emerald-500" : "bg-zinc-200 dark:bg-zinc-700"}`} />
34 )}
35 </div>
36 ))}
37 </div>
38
39 <h2 className="mb-1 text-xl font-bold text-zinc-900 dark:text-zinc-50">{steps[step].label}</h2>
40 <p className="mb-6 text-sm text-zinc-500 dark:text-zinc-400">Step {step + 1} of {steps.length}</p>
41
42 {step === 0 && (
43 <div className="space-y-4">
44 <div>
45 <label className="mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Email</label>
46 <input type="email" placeholder="you@example.com" value={form.email} onChange={(e) => setForm({ ...form, email: 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" />
47 </div>
48 <div>
49 <label className="mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Password</label>
50 <input type="password" placeholder="Create a strong password" value={form.password} onChange={(e) => setForm({ ...form, password: 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" />
51 </div>
52 </div>
53 )}
54
55 {step === 1 && (
56 <div className="space-y-4">
57 <div className="mx-auto flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br from-violet-100 to-indigo-100 dark:from-violet-900/30 dark:to-indigo-900/30">
58 <User className="h-8 w-8 text-violet-600 dark:text-violet-400" />
59 </div>
60 <div>
61 <label className="mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Full Name</label>
62 <input type="text" placeholder="John Doe" value={form.name} onChange={(e) => setForm({ ...form, name: 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" />
63 </div>
64 <div>
65 <label className="mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Bio</label>
66 <textarea placeholder="Tell us about yourself..." value={form.bio} onChange={(e) => setForm({ ...form, bio: e.target.value })} rows={3} 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" />
67 </div>
68 </div>
69 )}
70
71 {step === 2 && (
72 <div className="space-y-4">
73 <div>
74 <label className="mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300">Theme</label>
75 <div className="grid grid-cols-3 gap-2">
76 {["light", "dark", "system"].map((t) => (
77 <button key={t} onClick={() => setForm({ ...form, theme: t })} className={`rounded-lg border px-3 py-2 text-sm font-medium capitalize transition-all ${form.theme === t ? "border-violet-500 bg-violet-50 text-violet-700 dark:bg-violet-900/20 dark:text-violet-300" : "border-zinc-200 text-zinc-600 hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-400 dark:hover:bg-zinc-800"}`}>{t}</button>
78 ))}
79 </div>
80 </div>
81 <label className="flex items-center justify-between rounded-lg border border-zinc-200 p-4 dark:border-zinc-700">
82 <div>
83 <p className="text-sm font-medium text-zinc-900 dark:text-zinc-100">Email notifications</p>
84 <p className="text-xs text-zinc-500 dark:text-zinc-400">Receive updates about new features</p>
85 </div>
86 <input type="checkbox" checked={form.notifications} onChange={(e) => setForm({ ...form, notifications: e.target.checked })} className="h-4 w-4 accent-violet-600" />
87 </label>
88 </div>
89 )}
90
91 <div className="mt-8 flex items-center justify-between">
92 <button onClick={() => setStep(step - 1)} disabled={step === 0} className="flex items-center gap-1.5 rounded-lg px-4 py-2 text-sm font-medium text-zinc-600 transition-colors hover:bg-zinc-100 disabled:opacity-0 dark:text-zinc-400 dark:hover:bg-zinc-800">
93 <ArrowLeft className="h-4 w-4" /> Back
94 </button>
95 <button onClick={() => setStep(Math.min(step + 1, 2))} className="flex items-center gap-1.5 rounded-lg bg-violet-600 px-5 py-2.5 text-sm font-semibold text-white shadow-md transition-all hover:bg-violet-500 active:scale-[0.98]">
96 {step === 2 ? "Complete" : "Continue"} {step < 2 && <ArrowRight className="h-4 w-4" />}
97 </button>
98 </div>
99 </div>
100 );
101}

Related Auth & Onboarding Components

Command Palette

Search for a command to run...