Application
Password Strength Meter
Visual indicator for password strength with validation criteria.
Preview
•At least 8 characters
•Contains uppercase letter
•Contains number
•Contains special character
Code
"use client";
import { useEffect, useState, useMemo } from "react";
import { cn } from "@/lib/utils";
export interface PasswordStrengthMeterProps {
password: string;
minLength?: number;
className?: string;
barClassName?: string;
criteriaClassName?: string;
colors?: string[];
levels?: number;
customRequirements?: { label: string; test: (pass: string) => boolean }[];
}
export function PasswordStrengthMeter({
password,
minLength = 8,
className,
barClassName,
criteriaClassName,
colors = ["#dc2626", "#ea580c", "#16a34a", "#15803d"],
levels = 4,
customRequirements,
}: PasswordStrengthMeterProps) {
const [strength, setStrength] = useState<number>(0);
const [requirementsMet, setRequirementsMet] = useState<boolean[]>([]);
const defaultRequirements = useMemo(
() => [
{
label: `At least ${minLength} characters`,
test: (pass: string) => pass?.length >= minLength,
},
{
label: "Contains uppercase letter",
test: (pass: string) => /[A-Z]/.test(pass),
},
{
label: "Contains number",
test: (pass: string) => /[0-9]/.test(pass),
},
{
label: "Contains special character",
test: (pass: string) => /[^A-Za-z0-9]/.test(pass),
},
],
[minLength],
);
const requirements = customRequirements || defaultRequirements;
useEffect(() => {
const met = requirements.map((req) => req.test(password));
setRequirementsMet(met);
const metCount = met.filter(Boolean).length;
const newStrength = Math.min(
Math.floor((metCount / requirements.length) * levels),
levels,
);
setStrength(newStrength);
}, [password, requirements, levels]);
return (
<div
className={cn("space-y-3", className)}
role="region"
aria-label="Password strength meter"
>
<div className={cn("flex gap-1", barClassName)}>
{[...Array(levels)].map((_, i) => (
<div
key={i}
className="h-2 flex-1 rounded-full bg-muted transition-all"
style={{
backgroundColor: i < strength ? colors[strength - 1] : "",
}}
aria-hidden="true"
/>
))}
</div>
<div
className={cn("text-sm text-muted-foreground pt-2", criteriaClassName)}
>
{requirements.map((req, i) => (
<div
key={req.label}
className={cn(
"flex items-center gap-2",
requirementsMet[i] && "text-green-600",
)}
>
<span className="text-xs">•</span>
{req.label}
</div>
))}
</div>
</div>
);
}
Usage
import { PasswordStrengthMeter } from "@/components/library/application/PasswordStrengthMeter";
export const PasswordStrengthMeter = () => {
const [password, setPassword] = useState("");
return (
<div className="flex flex-col gap-4">
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-3 py-2 border rounded-md focus-visible:ring-0 focus-within:ring-0 focus:outline-white/10"
placeholder="Enter your password"
/>
<PasswordStrengthMeter password={password} />
</div>
);
};
Edit on GitHub
Last updated on