Set a new password
New password with strength rules and confirmation.
Built with
Choose a new password
For ayesha.khan@northwind.io. You'll be signed out on other devices.
set-a-new-password.tsx
"use client";
import { useState, type SyntheticEvent } from "react";
import { LuCheck, LuCircle, LuLock } from "react-icons/lu";
import { Alert, Button, Input, PasswordStrength } from "@nexera-ui/react";
const RULES = [
{ label: "At least 12 characters", test: (value: string) => value.length >= 12 },
{
label: "Upper and lower case letters",
test: (value: string) => /[a-z]/.test(value) && /[A-Z]/.test(value),
},
{ label: "At least one number", test: (value: string) => /\d/.test(value) },
{
label: "At least one symbol, such as ! or #",
test: (value: string) => /[^A-Za-z0-9]/.test(value),
},
];
const LEVELS = ["weak", "weak", "fair", "good", "strong"] as const;
/** Set a new password: strength meter, the rules it must meet, and a confirmation field. */
export default function SetANewPassword() {
const [password, setPassword] = useState("");
const [confirm, setConfirm] = useState("");
const [done, setDone] = useState(false);
const met = RULES.map((rule) => rule.test(password));
const allMet = met.every(Boolean);
const mismatch = confirm !== "" && confirm !== password;
const onSubmit = (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
if (allMet && !mismatch && confirm !== "") setDone(true); // replace with your update request
};
return (
<div className="@container grid min-h-[620px] place-items-center bg-page px-4 py-10">
<div className="grid w-full max-w-sm gap-6 rounded-xl border border-default bg-surface p-6 shadow-sm @sm:p-8">
<header className="grid gap-1">
<h2 className="text-heading-h3 text-primary">Choose a new password</h2>
<p className="text-body-default text-secondary">
For ayesha.khan@northwind.io. You'll be signed out on other devices.
</p>
</header>
{done ? (
<Alert
tone="success"
title="Password updated"
description="Sign in with your new password. This is a demo, so nothing was changed."
actions={
<a
href="#sign-in"
className="text-body-small text-brand-text underline underline-offset-4"
>
Go to sign in
</a>
}
/>
) : null}
<form className="grid gap-4" onSubmit={onSubmit}>
<div className="grid gap-2">
<Input
label="New password"
type="password"
name="new-password"
autoComplete="new-password"
leadingIcon={<LuLock />}
value={password}
onChange={setPassword}
isRequired
/>
<PasswordStrength
strength={password === "" ? "empty" : (LEVELS[met.filter(Boolean).length] ?? "weak")}
/>
</div>
<ul aria-label="Password rules" className="grid gap-1.5">
{RULES.map((rule, index) => (
<li
key={rule.label}
className={`flex items-center gap-2 text-body-small ${met[index] ? "text-status-success-fg" : "text-secondary"}`}
>
{met[index] ? (
<LuCheck aria-hidden="true" className="shrink-0" />
) : (
<LuCircle aria-hidden="true" className="shrink-0" />
)}
{rule.label}
<span className="sr-only">{met[index] ? "(met)" : "(not met yet)"}</span>
</li>
))}
</ul>
<Input
label="Confirm new password"
type="password"
name="confirm-password"
autoComplete="new-password"
leadingIcon={<LuLock />}
value={confirm}
onChange={setConfirm}
isInvalid={mismatch}
errorMessage="The passwords don't match."
isRequired
/>
<Button type="submit" size="lg" className="w-full" isDisabled={!allMet}>
Update password
</Button>
</form>
</div>
</div>
);
}
More sign in and account blocks
- Sign inEmail and password with remember me and a link to reset.
- Create accountName, email and password with a live strength meter.
- Two-step verificationEnter the 6-digit code sent by SMS or an authenticator app.
- Forgot passwordAsk for an email, then confirm the reset link was sent.
- Check your emailMagic link sent, with resend and change email actions.
- Session expiredAsk the person to sign in again without losing their place.
- Choose a workspacePick one of several organisations after signing in.