Back to Component Explorer

Plan Select Sign Up

signup

Pricing plan selector (Free/Pro/Team) with dynamic form per plan.

#signup#plan#pricing#saas
Live Interactive Preview

Get started

Choose a plan, then create your account

Component Source Code

Select language in the dropdown
PlanSignUp.tsx
1'use client';
2import React, { useState } from 'react';
3import { Mail, Lock, User, Building2, ArrowRight } from 'lucide-react';
4
5const PLANS = [
6 { id: 'free', label: 'Free', price: '$0', desc: 'Perfect to start' },
7 { id: 'pro', label: 'Pro', price: '$12', desc: 'For professionals' },
8 { id: 'team', label: 'Team', price: '$49', desc: 'Built for teams' },
9];
10
11export default function PlanSignUp() {
12 const [plan, setPlan] = useState('free');
13 const [loading, setLoading] = useState(false);
14 const submit = (e: React.FormEvent) => { e.preventDefault(); setLoading(true); setTimeout(() => setLoading(false), 1500); };
15 return (
16 <div className="w-full max-w-md rounded-2xl border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-8 shadow-lg">
17 <div className="mb-6">
18 <h2 className="text-xl font-bold text-zinc-900 dark:text-zinc-100">Get started</h2>
19 <p className="mt-1 text-xs text-zinc-500">Choose a plan, then create your account</p>
20 </div>
21
22 {/* Plan selector */}
23 <div className="grid grid-cols-3 gap-2 mb-6">
24 {PLANS.map((p) => (
25 <button
26 key={p.id}
27 type="button"
28 onClick={() => setPlan(p.id)}
29 className={`flex flex-col items-center rounded-xl border-2 p-3 text-center transition ${plan === p.id ? 'border-blue-600 bg-blue-50 dark:bg-blue-950' : 'border-zinc-200 dark:border-zinc-800 hover:border-zinc-300 dark:hover:border-zinc-700'}`}
30 >
31 <span className={`text-xs font-bold ${plan === p.id ? 'text-blue-600 dark:text-blue-400' : 'text-zinc-700 dark:text-zinc-300'}`}>{p.label}</span>
32 <span className={`text-base font-extrabold ${plan === p.id ? 'text-blue-600 dark:text-blue-400' : 'text-zinc-900 dark:text-zinc-100'}`}>{p.price}</span>
33 <span className="text-[10px] text-zinc-400 mt-0.5">{p.desc}</span>
34 </button>
35 ))}
36 </div>
37
38 <form onSubmit={submit} className="space-y-3.5">
39 <div className="grid grid-cols-2 gap-3">
40 <div>
41 <label className="block text-xs font-semibold text-zinc-700 dark:text-zinc-300 mb-1.5">First name</label>
42 <div className="relative">
43 <User className="absolute left-3 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-zinc-400" />
44 <input type="text" required placeholder="Alex" className="w-full rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800 py-2.5 pl-8 pr-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder:text-zinc-400 outline-none focus:border-blue-500 transition" />
45 </div>
46 </div>
47 <div>
48 <label className="block text-xs font-semibold text-zinc-700 dark:text-zinc-300 mb-1.5">Last name</label>
49 <input type="text" required placeholder="Johnson" className="w-full rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800 px-3 py-2.5 text-sm text-zinc-900 dark:text-zinc-100 placeholder:text-zinc-400 outline-none focus:border-blue-500 transition" />
50 </div>
51 </div>
52 <div>
53 <label className="block text-xs font-semibold text-zinc-700 dark:text-zinc-300 mb-1.5">Work email</label>
54 <div className="relative">
55 <Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-zinc-400" />
56 <input type="email" required placeholder="you@company.com" className="w-full rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800 py-2.5 pl-9 pr-3 text-sm text-zinc-900 dark:text-zinc-100 placeholder:text-zinc-400 outline-none focus:border-blue-500 transition" />
57 </div>
58 </div>
59 {plan !== 'free' && (
60 <div>
61 <label className="block text-xs font-semibold text-zinc-700 dark:text-zinc-300 mb-1.5">Company</label>
62 <div className="relative">
63 <Building2 className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-zinc-400" />
64 <input type="text" placeholder="Acme Corp" className="w-full rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800 py-2.5 pl-9 pr-3 text-sm text-zinc-900 dark:text-zinc-100 placeholder:text-zinc-400 outline-none focus:border-blue-500 transition" />
65 </div>
66 </div>
67 )}
68 <div>
69 <label className="block text-xs font-semibold text-zinc-700 dark:text-zinc-300 mb-1.5">Password</label>
70 <div className="relative">
71 <Lock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-zinc-400" />
72 <input type="password" required placeholder="Create password" className="w-full rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800 py-2.5 pl-9 pr-3 text-sm text-zinc-900 dark:text-zinc-100 placeholder:text-zinc-400 outline-none focus:border-blue-500 transition" />
73 </div>
74 </div>
75 <button type="submit" disabled={loading} className="flex w-full items-center justify-center gap-2 rounded-lg bg-blue-600 hover:bg-blue-700 py-2.5 text-sm font-semibold text-white transition disabled:opacity-60">
76 {loading ? 'Creating...' : <><span>Start {PLANS.find(p => p.id === plan)?.label} Plan</span><ArrowRight className="h-4 w-4" /></>}
77 </button>
78 </form>
79 </div>
80 );
81}

Installation & Integration Guide

Step 1: Tailwind CSS v4 Setup
Tailwind Docs

If you do not have Tailwind installed yet, run this installation command:

Terminal
npm install tailwindcss @tailwindcss/postcss postcss

Then add the Tailwind directive to your main stylesheet (globals.css):

@import "tailwindcss";

Step 2: Install Icon Dependencies

npm install lucide-react react-icons

Step 3: Add Component File

Create a new file in your project at src/components/signup/PlanSignUp.tsx and paste the copied code from the source viewer above.

Step 4: Usage Example

ExamplePage.tsx
1import PlanSelectSignUp from '@/components/signup/PlanSignUp';
2
3export default function ExamplePage() {
4 return (
5 <main className="p-8 flex items-center justify-center min-h-screen">
6 <PlanSelectSignUp />
7 </main>
8 );
9}