Not affiliated with official shadcn/ui. Visit ui.shadcn.com for official docs.
Unlock this block—Get Pro at 60% offReact Dialog Block Change Password
Change password dialog with current password verification, new password input, strength indicator, and confirmation matching
Looking to implement shadcn/ui blocks?
Join our Discord community for help from other developers.
Update passwords securely with validation. This React change password dialog provides current password verification, new password input with strength indicator, confirmation field with match validation, and clear requirements display. Built with shadcn/ui Dialog, Input, Button, Progress, and Label components using Tailwind CSS, users change passwords confidently with real-time feedback. Enter current, set new, confirm match—perfect for account settings, security pages, user profiles, or any Next.js application where secure password updates require proper validation and user guidance.
"use client";import { useState, useMemo } from "react";import { Lock, Eye, EyeOff, Check, X, AlertCircle, Loader2, ShieldCheck,} from "lucide-react";import { Button } from "@/components/ui/button";import { Badge } from "@/components/ui/badge";import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/ui/dialog";import { Input } from "@/components/ui/input";import { Label } from "@/components/ui/label";import { Progress } from "@/components/ui/progress";import { cn } from "@/lib/utils";type PasswordStrength = "weak" | "medium" | "strong";const requirements = [ { id: "length", label: "At least 8 characters", test: (p: string) => p.length >= 8 }, { id: "uppercase", label: "One uppercase letter", test: (p: string) => /[A-Z]/.test(p) }, { id: "lowercase", label: "One lowercase letter", test: (p: string) => /[a-z]/.test(p) }, { id: "number", label: "One number", test: (p: string) => /[0-9]/.test(p) }, { id: "special", label: "One special character", test: (p: string) => /[!@#$%^&*(),.?":{}|<>]/.test(p) },];export const title = "React Dialog Block Change Password";export default function DialogChangePassword() { const [open, setOpen] = useState(false); const [currentPassword, setCurrentPassword] = useState(""); const [newPassword, setNewPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [showCurrent, setShowCurrent] = useState(false); const [showNew, setShowNew] = useState(false); const [showConfirm, setShowConfirm] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [isSuccess, setIsSuccess] = useState(false); const [error, setError] = useState(""); const metRequirements = useMemo(() => { return requirements.filter((req) => req.test(newPassword)); }, [newPassword]); const strength = useMemo((): PasswordStrength => { const count = metRequirements.length; if (count <= 2) return "weak"; if (count <= 4) return "medium"; return "strong"; }, [metRequirements]); const strengthConfig = { weak: { label: "Weak", color: "text-destructive", progress: 33 }, medium: { label: "Medium", color: "text-yellow-500", progress: 66 }, strong: { label: "Strong", color: "text-primary", progress: 100 }, }; const passwordsMatch = newPassword === confirmPassword && confirmPassword.length > 0; const allRequirementsMet = metRequirements.length === requirements.length; const canSubmit = currentPassword && allRequirementsMet && passwordsMatch; const handleSubmit = async () => { if (!canSubmit) return; setError(""); setIsSubmitting(true); // Simulate API call await new Promise((resolve) => setTimeout(resolve, 1500)); // Simulate success (in real app, would verify current password) setIsSubmitting(false); setIsSuccess(true); }; const handleClose = () => { setOpen(false); setTimeout(() => { setCurrentPassword(""); setNewPassword(""); setConfirmPassword(""); setIsSuccess(false); setError(""); }, 200); }; return ( <Dialog open={open} onOpenChange={setOpen}> <div className="flex min-h-[350px] items-center justify-center"> <DialogTrigger asChild> <Button variant="outline"> <Lock className="mr-2 h-4 w-4" /> Change Password </Button> </DialogTrigger> </div> <DialogContent className="sm:max-w-md"> <DialogHeader> <DialogTitle className="flex items-center gap-2"> <Lock className="h-5 w-5" /> Change Password </DialogTitle> <DialogDescription> Enter your current password and choose a new one. </DialogDescription> </DialogHeader> {isSuccess ? ( <div className="flex flex-col items-center py-8 text-center"> <div className="rounded-full border-2 border-primary p-3 mb-4"> <ShieldCheck className="h-6 w-6 text-primary" /> </div> <h3 className="font-semibold">Password Changed</h3> <p className="text-sm text-muted-foreground mt-1"> Your password has been updated successfully. </p> <Button className="mt-4" onClick={handleClose}> Done </Button> </div> ) : ( <> <div className="space-y-4 py-4"> {error && ( <div className="flex items-center gap-2 text-sm text-destructive"> <AlertCircle className="h-4 w-4" /> {error} </div> )} <div className="space-y-2"> <Label htmlFor="current-password">Current Password</Label> <div className="relative"> <Input id="current-password" type={showCurrent ? "text" : "password"} value={currentPassword} onChange={(e) => setCurrentPassword(e.target.value)} placeholder="Enter current password" className="pr-10" /> <Button type="button" variant="ghost" size="icon" className="absolute right-0 top-0 h-full px-3 hover:bg-transparent" onClick={() => setShowCurrent(!showCurrent)} > {showCurrent ? ( <EyeOff className="h-4 w-4 text-muted-foreground" /> ) : ( <Eye className="h-4 w-4 text-muted-foreground" /> )} </Button> </div> </div> <div className="space-y-2"> <Label htmlFor="new-password">New Password</Label> <div className="relative"> <Input id="new-password" type={showNew ? "text" : "password"} value={newPassword} onChange={(e) => setNewPassword(e.target.value)} placeholder="Enter new password" className="pr-10" /> <Button type="button" variant="ghost" size="icon" className="absolute right-0 top-0 h-full px-3 hover:bg-transparent" onClick={() => setShowNew(!showNew)} > {showNew ? ( <EyeOff className="h-4 w-4 text-muted-foreground" /> ) : ( <Eye className="h-4 w-4 text-muted-foreground" /> )} </Button> </div> {newPassword && ( <div className="space-y-2"> <div className="flex items-center justify-between text-xs"> <span className="text-muted-foreground">Strength:</span> <span className={strengthConfig[strength].color}> {strengthConfig[strength].label} </span> </div> <Progress value={strengthConfig[strength].progress} className="h-1.5" /> </div> )} </div> <div className="space-y-2"> <Label htmlFor="confirm-password">Confirm New Password</Label> <div className="relative"> <Input id="confirm-password" type={showConfirm ? "text" : "password"} value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} placeholder="Confirm new password" className={cn( "pr-10", confirmPassword && !passwordsMatch && "border-destructive" )} /> <Button type="button" variant="ghost" size="icon" className="absolute right-0 top-0 h-full px-3 hover:bg-transparent" onClick={() => setShowConfirm(!showConfirm)} > {showConfirm ? ( <EyeOff className="h-4 w-4 text-muted-foreground" /> ) : ( <Eye className="h-4 w-4 text-muted-foreground" /> )} </Button> </div> {confirmPassword && ( <p className={cn( "text-xs flex items-center gap-1", passwordsMatch ? "text-primary" : "text-destructive" )}> {passwordsMatch ? ( <> <Check className="h-3 w-3" /> Passwords match </> ) : ( <> <X className="h-3 w-3" /> Passwords do not match </> )} </p> )} </div> <div className="rounded-lg border p-3 space-y-2"> <p className="text-xs font-medium text-muted-foreground">Requirements:</p> <div className="grid grid-cols-1 gap-1"> {requirements.map((req) => { const isMet = req.test(newPassword); return ( <div key={req.id} className={cn( "text-xs flex items-center gap-2", isMet ? "text-primary" : "text-muted-foreground" )} > {isMet ? ( <Check className="h-3 w-3" /> ) : ( <X className="h-3 w-3" /> )} {req.label} </div> ); })} </div> </div> </div> <DialogFooter> <Button variant="outline" onClick={handleClose}> Cancel </Button> <Button onClick={handleSubmit} disabled={!canSubmit || isSubmitting}> {isSubmitting ? ( <> <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Updating... </> ) : ( <> <Lock className="mr-2 h-4 w-4" /> Update Password </> )} </Button> </DialogFooter> </> )} </DialogContent> </Dialog> );}Installation
npx shadcn@latest add https://www.shadcn.io/registry/dialog-change-password.jsonnpx shadcn@latest add https://www.shadcn.io/registry/dialog-change-password.jsonpnpm dlx shadcn@latest add https://www.shadcn.io/registry/dialog-change-password.jsonbunx shadcn@latest add https://www.shadcn.io/registry/dialog-change-password.jsonRelated blocks you will also like
React Dialog Block Password Generator
Generate passwords
React Dialog Block Two Factor Setup
Security settings
React Dialog Block Password Confirm
Verify identity
React Dialog Block Edit Profile
Account settings
React Dialog Block Success Confirmation
Password changed
React Dialog Block Session Timeout
Re-authenticate