Not affiliated with official shadcn/ui. Visit ui.shadcn.com for official docs.
Unlock this block—Get Pro at 60% offReact Dialog Block Register
User registration dialog with email, password, name fields, social signup options, terms acceptance, and validation
Looking to implement shadcn/ui blocks?
Join our Discord community for help from other developers.
Register new users with comprehensive signup flow. This React registration dialog provides name, email, and password fields with validation, password strength indicator, social signup buttons for Google and GitHub, terms and privacy acceptance checkbox, and sign in link for existing users. Built with shadcn/ui Dialog, Input, Button, Checkbox, Label, and Progress components using Tailwind CSS, users create accounts quickly with their preferred method. Enter details, accept terms, create account—perfect for web applications, SaaS products, member portals, or any Next.js application requiring user registration with multiple signup options.
"use client";import { useState, useMemo } from "react";import { UserPlus, Mail, Lock, Eye, EyeOff, Loader2, User, Check, X,} from "lucide-react";import { Button } from "@/components/ui/button";import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/ui/dialog";import { Input } from "@/components/ui/input";import { Label } from "@/components/ui/label";import { Checkbox } from "@/components/ui/checkbox";import { Separator } from "@/components/ui/separator";import { Progress } from "@/components/ui/progress";import { cn } from "@/lib/utils";type PasswordStrength = { score: number; label: string; color: string;};const getPasswordStrength = (password: string): PasswordStrength => { let score = 0; if (password.length >= 8) score += 25; if (password.length >= 12) score += 15; if (/[a-z]/.test(password) && /[A-Z]/.test(password)) score += 20; if (/\d/.test(password)) score += 20; if (/[^a-zA-Z0-9]/.test(password)) score += 20; if (score < 40) return { score, label: "Weak", color: "bg-destructive" }; if (score < 70) return { score, label: "Fair", color: "bg-yellow-500" }; if (score < 90) return { score, label: "Good", color: "bg-blue-500" }; return { score, label: "Strong", color: "bg-green-500" };};const passwordRequirements = [ { label: "At least 8 characters", test: (p: string) => p.length >= 8 }, { label: "Uppercase letter", test: (p: string) => /[A-Z]/.test(p) }, { label: "Lowercase letter", test: (p: string) => /[a-z]/.test(p) }, { label: "Number", test: (p: string) => /\d/.test(p) },];export const title = "React Dialog Block Register";export default function DialogRegister() { const [open, setOpen] = useState(false); const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [acceptTerms, setAcceptTerms] = useState(false); const [isLoading, setIsLoading] = useState(false); const passwordStrength = useMemo( () => getPasswordStrength(password), [password] ); const isValid = name.trim().length >= 2 && email.includes("@") && password.length >= 8 && acceptTerms; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!isValid) return; setIsLoading(true); await new Promise((resolve) => setTimeout(resolve, 1500)); setIsLoading(false); setOpen(false); }; const handleSocialSignup = async (provider: string) => { setIsLoading(true); await new Promise((resolve) => setTimeout(resolve, 1000)); setIsLoading(false); // In real app: redirect to OAuth provider }; const handleClose = () => { setOpen(false); setTimeout(() => { setName(""); setEmail(""); setPassword(""); setAcceptTerms(false); }, 200); }; return ( <Dialog open={open} onOpenChange={setOpen}> <div className="flex min-h-[350px] items-center justify-center"> <DialogTrigger asChild> <Button variant="outline"> <UserPlus className="mr-2 h-4 w-4" /> Create Account </Button> </DialogTrigger> </div> <DialogContent className="sm:max-w-md"> <DialogHeader> <DialogTitle className="text-center">Create an account</DialogTitle> <DialogDescription className="text-center"> Get started with your free account </DialogDescription> </DialogHeader> <div className="space-y-4 py-4"> {/* Social Signup Buttons */} <div className="grid grid-cols-2 gap-3"> <Button variant="outline" onClick={() => handleSocialSignup("google")} disabled={isLoading} > <svg className="mr-2 h-4 w-4" viewBox="0 0 24 24"> <path fill="currentColor" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" /> <path fill="currentColor" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" /> <path fill="currentColor" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" /> <path fill="currentColor" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" /> </svg> Google </Button> <Button variant="outline" onClick={() => handleSocialSignup("github")} disabled={isLoading} > <svg className="mr-2 h-4 w-4" fill="currentColor" viewBox="0 0 24 24"> <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /> </svg> GitHub </Button> </div> <div className="relative"> <div className="absolute inset-0 flex items-center"> <Separator className="w-full" /> </div> <div className="relative flex justify-center text-xs uppercase"> <span className="bg-background px-2 text-muted-foreground"> Or continue with email </span> </div> </div> {/* Registration Form */} <form onSubmit={handleSubmit} className="space-y-4"> {/* Name */} <div className="space-y-2"> <Label htmlFor="name">Full Name</Label> <div className="relative"> <User className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <Input id="name" placeholder="John Doe" value={name} onChange={(e) => setName(e.target.value)} className="pl-9" disabled={isLoading} /> </div> </div> {/* Email */} <div className="space-y-2"> <Label htmlFor="register-email">Email</Label> <div className="relative"> <Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <Input id="register-email" type="email" placeholder="[email protected]" value={email} onChange={(e) => setEmail(e.target.value)} className="pl-9" disabled={isLoading} /> </div> </div> {/* Password */} <div className="space-y-2"> <Label htmlFor="register-password">Password</Label> <div className="relative"> <Lock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <Input id="register-password" type={showPassword ? "text" : "password"} placeholder="Create a password" value={password} onChange={(e) => setPassword(e.target.value)} className="pl-9 pr-9" disabled={isLoading} /> <Button type="button" variant="ghost" size="icon" className="absolute right-0 top-0 h-full px-3 hover:bg-transparent" onClick={() => setShowPassword(!showPassword)} > {showPassword ? ( <EyeOff className="h-4 w-4 text-muted-foreground" /> ) : ( <Eye className="h-4 w-4 text-muted-foreground" /> )} </Button> </div> {/* Password Strength */} {password && ( <div className="space-y-2"> <div className="flex items-center gap-2"> <Progress value={passwordStrength.score} className={cn("h-1.5 flex-1", passwordStrength.color)} /> <span className="text-xs text-muted-foreground"> {passwordStrength.label} </span> </div> <div className="grid grid-cols-2 gap-1"> {passwordRequirements.map((req) => { const passed = req.test(password); return ( <div key={req.label} className="flex items-center gap-1 text-xs" > {passed ? ( <Check className="h-3 w-3 text-green-500" /> ) : ( <X className="h-3 w-3 text-muted-foreground" /> )} <span className={ passed ? "text-foreground" : "text-muted-foreground" } > {req.label} </span> </div> ); })} </div> </div> )} </div> {/* Terms Acceptance */} <div className="flex items-start gap-2"> <Checkbox id="terms" checked={acceptTerms} onCheckedChange={(checked) => setAcceptTerms(checked === true)} disabled={isLoading} className="mt-0.5" /> <Label htmlFor="terms" className="text-sm font-normal leading-tight cursor-pointer"> I agree to the{" "} <Button variant="link" className="h-auto p-0 text-sm"> Terms of Service </Button>{" "} and{" "} <Button variant="link" className="h-auto p-0 text-sm"> Privacy Policy </Button> </Label> </div> <Button type="submit" className="w-full" disabled={!isValid || isLoading}> {isLoading ? ( <> <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Creating account... </> ) : ( <> <UserPlus className="mr-2 h-4 w-4" /> Create account </> )} </Button> </form> <p className="text-center text-sm text-muted-foreground"> Already have an account?{" "} <Button variant="link" className="h-auto p-0 text-sm"> Sign in </Button> </p> </div> </DialogContent> </Dialog> );}Installation
npx shadcn@latest add https://www.shadcn.io/registry/dialog-register.jsonnpx shadcn@latest add https://www.shadcn.io/registry/dialog-register.jsonpnpm dlx shadcn@latest add https://www.shadcn.io/registry/dialog-register.jsonbunx shadcn@latest add https://www.shadcn.io/registry/dialog-register.jsonRelated blocks you will also like
React Dialog Block Login
User login
React Dialog Block Email Verification
Verify email
React Dialog Block Terms Accept
Terms acceptance
React Dialog Block Onboarding Welcome
Post-signup onboarding
React Dialog Block Connect Account
Social accounts
React Dialog Block Success Confirmation
Registration success