Inputs
Password Input
A styled password field with a show/hide toggle and an optional heuristic strength meter.
Preview in your theme
Loading preview…
Installation
npx shadcn@latest add https://ui.zyeon.ai/r/password-input.jsonPrompt
The prompt behind this component — paste it into your AI assistant to recreate or adapt it.
Build a React + TypeScript + Tailwind "PasswordInput" component (lucide-react Eye/EyeOff).
Contract
- Export a forwardRef component whose ref points at the inner <input>;
props extend InputHTMLAttributes<HTMLInputElement> minus "type" (the
component owns type, switching it between "password" and "text").
- Add showStrength?: boolean (default false) — renders a 4-segment
heuristic strength meter below the field.
- Fully supports both controlled (value + onChange) and uncontrolled
(defaultValue) usage — value/defaultValue/onChange pass straight
through to the inner input, unmodified in shape.
Behavior
- A ghost icon button on the right toggles the input's type between
"password" and "text" (Eye when hidden, EyeOff when visible); type="button"
so it never submits a form, aria-label switches between "Show password"
and "Hide password", and it stays in the natural tab order.
- Strength scoring is 0-4, one point each for: length >= 8, mixed upper
+ lower case, at least one digit, at least one symbol. It runs against
whatever the current value is — read straight from the value prop when
controlled, or mirrored into internal state on every onChange when
uncontrolled (this mirror never changes controlled/uncontrolled
behavior, it only gives the meter something to score).
- Score renders as 4 small segments + one label: 0-1 "Weak", 2 "Fair",
3 "Good", 4 "Strong". This is a heuristic hint only — real validation
belongs in a zod schema on the form, not in this component.
- disabled dims the field and disables both the input and the toggle button.
Rendering & styling
- Outer field mimics the shadcn Input: flex h-9 items-center rounded-md
border border-input bg-transparent px-3, focus-within:border-ring +
focus-within:ring-ring/50 so the whole box focuses as one field; the
inner <input> itself is borderless and bg-transparent.
- Strength segments: unfilled bg-muted; filled color depends on the score
bucket via inline style — var(--destructive) for 1-2, var(--chart-4)
for 3, var(--chart-2) for 4. Semantic tokens only, no hardcoded hex/oklch.
- Toggle button: ghost icon button (hover:bg-muted, focus-visible ring);
merge the consumer className via cn() onto the field container.
Customization levers
- Strength rule set: swap scorePassword() for a zxcvbn call (or add more
rules) — it only needs to keep returning 0-4 for the existing 4-segment
UI.
- Segment count / labels: change the segment array length and
STRENGTH_LABEL together for a different granularity.
- react-hook-form: wrap in a Controller (or spread {...field}) — the
component's value/onChange contract is already a plain controlled
input, no extra adapter needed; pair it with a zod schema
(e.g. z.string().min(8)) for the actual pass/fail validation instead
of relying on the UI meter.Concepts
- Controlled/uncontrolled mirroring — when a
valueis supplied it's the only source of truth; otherwise an internal shadow state tracks each keystroke purely so the strength meter has something to read. - Heuristic strength scoring — four independent rules (length, case mix, digit, symbol) each contribute one point; it's a fast UX signal, not a security guarantee.
- Progressive disclosure via visibility toggle — the field defaults to masked and only reveals plaintext on an explicit, reversible user action.
- Decoupled validation — the strength meter never blocks submission; real pass/fail rules belong in the form's zod schema, not in this component.