Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
  1. Home
  2. Blog
  3. Component guides
  4. Shadcn Password Input: Show/Hide, Strength Meter, Caps Lock
Auf Deutsch lesen
  1. Blog
  2. Component guides
  3. Shadcn Password Input: Show/Hide, Strength Meter, Caps Lock
Component guides
Component guides

Shadcn Password Input: Show/Hide, Strength Meter, Caps Lock

SR
Serban Rusu · Founder of Wingo UI
Nov 5, 2026 · 9 min read

On this page

0%
  1. What should a shadcn password input handle?
  2. How do I build a shadcn password input show/hide toggle that keeps the caret?
    1. Should the eye button be in the Tab order?
    2. Should the password hide again on submit?
  3. How does the Caps Lock warning work?
  4. How do I add a react password strength meter?
    1. Is the default password strength indicator good enough?
    2. What do the current guidelines say about password rules?
  5. What else trips people up?
  6. Where does the password input fit in a whole sign-up form?
  7. Next step
  8. FAQ
    1. Does shadcn/ui have a password input component?
    2. Should the show password button have tabIndex -1?
    3. How do I build a React password strength meter?
    4. How do I detect Caps Lock in a password field?
    5. Should a sign-up form force uppercase, numbers and symbols?

TL;DR

shadcn/ui has no password input as of November 5, 2026, so you build one on top of Input: a focusable toggle button with aria-pressed that switches type between password and text without moving the caret, the right autocomplete value, and spellcheck and autocapitalize off. A sign-up field adds a strength meter with a politely announced verdict, a rules checklist linked through aria-describedby, and a Caps Lock warning read from getModifierState. The Wingo UI Password Input (Pro) ships all of this in one component.

Published Nov 5, 2026

You need a password field with an eye icon, maybe a strength meter for sign-up, and you search for a shadcn password input. The first surprise: shadcn/ui does not ship one. As of November 5, 2026 the shadcn/ui component list (opens in a new tab) has Input, Input Group and Field, but no password input, so what you find are community snippets built on Input. The eye icon is the easy part. The details that decide whether the field works for real people are where the caret goes when the type switches, whether a screen reader hears the state, what password managers are told, and what happens with Caps Lock on.

This guide walks through those details with the Password Input from Wingo UI, the library we build, so we are not neutral. Outside facts link to their sources.

What should a shadcn password input handle?

Our checklist for a login field, before anything about strength:

  • A real reveal button. A <button type="button"> in the Tab order, an aria-label that says what it does ("Show password", "Hide password") and aria-pressed for the current state.
  • The caret stays put. Switching type from password to text drops the selection in some browsers, so the caret jumps to the end or the start. Save selectionStart, selectionEnd and the direction before the switch and restore them after.
  • Password managers get the right hint. autocomplete="current-password" on login, "new-password" on sign-up and password change.
  • The browser leaves the text alone. spellCheck={false}, autoCapitalize="none" and autoCorrect="off", so revealed text is not underlined, capitalized or sent to a spellchecker. The GOV.UK password input (opens in a new tab) sets spellcheck and autocapitalize the same way (checked November 5, 2026).
  • Caps Lock is visible. A short warning under the box while Caps Lock is on and the field has focus.
  • Phones work. A 16px font so iOS does not zoom, 44px hit areas for the icon buttons, and an enterKeyHint that matches the next step ("go" on login, "next" on sign-up).

The Wingo UI component sets every item on that list by default:

bash
npx wingo-ui@latest add password-input
tsx
import { PasswordInput } from "@/components/ui/password-input"
export function PasswordField() {
return (
<PasswordInput
name="password"
label="Password"
placeholder="Your password"
required
fieldProps={{ labelAction: <a href="/forgot-password">Forgot password?</a> }}
/>
)
}

label, description and error come from the shared Field wrapper, so the label is linked with htmlFor, the hint with aria-describedby, and an error replaces the hint and sets aria-invalid. fieldProps.labelAction puts the "Forgot password?" link on the label row, where people look for it.

Type a password, move the caret to the middle, then tap the eye: the dots turn into text and the caret stays where it was
$ npx wingo-ui@latest add password-input
ProPassword Input docs

How do I build a shadcn password input show/hide toggle that keeps the caret?

A typical shadcn password input show hide snippet is a useState boolean, an absolutely positioned button and type={visible ? "text" : "password"}. That works until someone edits the middle of a long password, reveals it to check, and keeps typing at the wrong spot. The fix is three steps:

  1. On click, read selectionStart, selectionEnd and selectionDirection while the input still has focus.
  2. Flip the state.
  3. In a layout effect on the visibility, call setSelectionRange with the saved values, then once more on the next animation frame, because Chrome resets the selection a second time after a real mouse click.

The button also calls preventDefault() on pointerdown and mousedown, so the click does not blur the input first; otherwise there is no focused selection left to save. The Wingo component does all of this internally; you control visibility only when you need to, for example one checkbox that reveals two fields:

tsx
"use client"
import { useState } from "react"
import { Checkbox } from "@/components/ui/checkbox"
import { PasswordInput } from "@/components/ui/password-input"
export function RouterPasswords() {
const [visible, setVisible] = useState(false)
return (
<div className="flex flex-col gap-4">
<PasswordInput label="Network password" visible={visible} onVisibleChange={setVisible} showToggle={false} />
<PasswordInput label="Admin password" visible={visible} onVisibleChange={setVisible} showToggle={false} />
<Checkbox label="Show passwords" checked={visible} onCheckedChange={(c) => setVisible(c === true)} />
</div>
)
}

Should the eye button be in the Tab order?

Yes. Some snippets add tabIndex={-1} to keep Tab moving from the password to the submit button. That removes the show password toggle for keyboard and switch users, the people who most need to check what they typed. Keep it focusable, give it an aria-label, and set aria-pressed, so a screen reader announces it as a toggle button with its label and pressed state. In the Wingo component the label follows the state ("Show password" while hidden, "Hide password" while shown), and the button carries aria-controls pointing at the input. Change the words with showLabel and hideLabel, and swap the Lucide Eye and EyeOff icons with showIcon and hideIcon.

Should the password hide again on submit?

GOV.UK switches the field back to type="password" when the form submits, so the browser does not remember the value as a previous entry and suggest it in ordinary text fields. The Wingo component does not do this by itself. Control visible and hide the password in your submit handler; flushSync makes sure the input is back to type="password" before the browser reads the form:

tsx
import { flushSync } from "react-dom"
<form onSubmit={() => flushSync(() => setVisible(false))}>
<PasswordInput name="password" label="Password" visible={visible} onVisibleChange={setVisible} />
</form>

How does the Caps Lock warning work?

KeyboardEvent.getModifierState("CapsLock") returns the current state, and so does the same method on mouse events. Read it in keydown, keyup and mousedown while the field has focus, show a warning, and hide it on blur. The Wingo component keeps a role="status" region mounted under the box so the warning is announced when it appears, and adds it to aria-describedby while it shows. Phones do not report Caps Lock, so on a touch screen the warning simply never appears. Turn it off with capsLockWarning={false} or reword it with capsLockLabel.

How do I add a react password strength meter?

Pass strength and the field becomes a sign-up field: four bars that fill as you type, a verdict in words next to them, a rules checklist that opens on focus, and autocomplete="new-password". Add generate for a wand button that fills a random password.

Type a password and watch the bars and the checklist react, then tap the wand to generate a 16-character password
$ npx wingo-ui@latest add password-input
ProPassword Input docs
tsx
"use client"
import { useState } from "react"
import { PasswordInput, passwordRules } from "@/components/ui/password-input"
const rules = passwordRules({
minLength: 10,
labels: {
length: (n) => `At least ${n} characters`,
lower: "One lowercase letter",
upper: "One uppercase letter",
number: "One digit",
symbol: "One symbol",
},
})
export function NewPassword() {
const [password, setPassword] = useState("")
return (
<PasswordInput
name="password"
label="Password"
value={password}
onValueChange={setPassword}
strength
rules={rules}
showRules="focus"
strengthLabels={["Too weak", "Weak", "Okay", "Good", "Strong"]}
generate
required
/>
)
}

A few choices in that code are deliberate:

  • The verdict is text, not only color. Red, amber and green bars mean nothing to someone with color blindness. The label next to the bars says "Weak" or "Strong".
  • The bars are hidden from screen readers and the verdict goes to a polite live region 500ms after typing stops, so a screen reader user hears "Password strength: Good" once instead of a new message on every key.
  • Each rule announces its state. The checklist is linked to the input with aria-describedby while it is open, and each row ends with a visually hidden "met" or "not met" (ruleMetLabel, ruleUnmetLabel).
  • The checklist does not collapse on blur while there is text. If it did, the submit button below it would jump up just as a thumb reaches for it.
  • The generator uses crypto.getRandomValues with rejection sampling, skips look-alike characters (0, O, l, 1, I), guarantees one of each character class, and reveals the result until focus leaves the field.

Is the default password strength indicator good enough?

No, not on its own. It is a guide. The default scorePassword returns 0 for empty, short or common passwords, adds a point per met rule beyond length (up to 3), adds one for 12 or more characters, and takes one away for repeats like aaa or runs like 1234. That catches the obvious cases. It does not know that Summer2026! is a pattern attackers try early: it has all four character classes, so the default meter rates it Good.

When the score gates sign-up, swap in a real estimator through the score prop. With zxcvbn-ts (opens in a new tab) (setup checked November 5, 2026):

tsx
import { ZxcvbnFactory } from "@zxcvbn-ts/core"
import * as common from "@zxcvbn-ts/language-common"
import * as en from "@zxcvbn-ts/language-en"
const zxcvbn = new ZxcvbnFactory({
translations: en.translations,
graphs: common.adjacencyGraphs,
dictionary: { ...common.dictionary, ...en.dictionary },
})
export function SignUpPassword() {
return <PasswordInput name="password" label="Password" strength score={(p) => zxcvbn.check(p).score} />
}

The dictionaries are large, so load this only on the sign-up route.

What do the current guidelines say about password rules?

They say length and a blocklist, not character mixes. The revision 4 text of NIST SP 800-63B (opens in a new tab) (section 3.1.1, checked November 5, 2026) says verifiers shall not impose composition rules, should permit at least 64 characters, should offer an option to display the password, shall allow password managers and autofill, and should permit paste. It requires at least 15 characters when the password is the only factor and 8 when it is part of multi-factor sign-in, and it requires comparing new passwords against a blocklist of common, expected and compromised values. In practice:

  • Keep a length rule and drop the uppercase, digit and symbol rules: rules={passwordRules({ minLength: 15 }).filter((r) => r.id === "length")}. Use a real estimator for the meter too: the default score adds a point per class rule in the list, so with only the length rule it never climbs past 1.
  • Never block paste, and never set maxLength below 64.
  • Run the blocklist check on the server. The client meter is advice; the server decides.

What else trips people up?

Four smaller things: a confirm field, password manager icons, autofill and login error copy.

  • A confirm password field. GOV.UK says a second field is not necessary, because the show toggle lets people check what they typed. If you keep one, compare on blur, not on every keystroke, and give each toggle its own label.
  • Password manager icons over your eye. Some password manager extensions draw their own icon at the right edge of the input element. In the Wingo component the eye and wand buttons sit outside the native input, so that icon lands before the toggle instead of on top of it.
  • The meter flashing on autofill. A manager can fill the field before the person touches it. The component keeps scoring the starting value until the field gets focus, so an autofilled password does not make the bars jump on page load.
  • Validation copy. For login, keep one generic error for a wrong email or password. Our shadcn login page guide covers the reasons and the full flow.

Where does the password input fit in a whole sign-up form?

It is one field in a block that already handles the rest. A password field is one step of a form that also needs an email, a submit state and errors from the server. The Register Page block wires the Password Input to react-hook-form and zod: the same rules feed the checklist and the schema, so the checklist and the validation never disagree, and a minimum score (2 by default, set with passwordMinScore) blocks weak passwords before submit. The Login Page block uses the login variant with the "Forgot password?" action. For the rest of the form, the Input and Field components share the same box, sizes and error anatomy, and our React form components guide covers labels, zod and mobile keyboards across all of them. More component guides live in the components category.

Next step

Field and Input are free. The Password Input and the Register Page block are part of Wingo UI Pro (pricing). Open the Password Input docs, try the six examples there (sign-up rules, Caps Lock, generator, confirm, controlled visibility, sizes), and install it with npx wingo-ui@latest add password-input when it fits your form.

Components in this post

  • Password Input

    A password field with a caret-safe reveal toggle, a Caps Lock warning and, for sign-up, a strength meter, rules checklist and generator.

    Pro
  • Field

    A React form field wrapper every control shares: label, hint, animated error and counter, fieldsets, and the box recipe all inputs use.

    Free
  • Register Page

    A React register page for a CRM sign-up: an account with password rules, company details from the CUI, an email code and a welcome step.

    Pro

FAQ

Does shadcn/ui have a password input component?

No. As of November 5, 2026 the shadcn/ui component list has Input, Input Group and Field but no password input, so every shadcn password input you find is a community snippet or a library component built on top of Input.

Should the show password button have tabIndex -1?

No. Keyboard and screen reader users need to reach it, so keep it a real button in the Tab order with an aria-label and aria-pressed that reflect the current state. The GOV.UK password input uses a native button element for its show toggle, which is focusable by default.

How do I build a React password strength meter?

Score the password from 0 to 4, fill one bar per point, and print the verdict in words next to the bars so color is not the only signal. Announce the verdict through a polite live region after typing stops, and use a real estimator such as zxcvbn-ts when the score gates sign-up.

How do I detect Caps Lock in a password field?

Call event.getModifierState("CapsLock") in keydown, keyup and mousedown handlers while the field has focus, and hide the warning on blur. Phones do not report the Caps Lock state, so the warning only appears with a hardware keyboard.

Should a sign-up form force uppercase, numbers and symbols?

No. NIST SP 800-63B revision 4 says verifiers shall not impose composition rules and shall compare new passwords against a blocklist of common and compromised values. A length rule, a server-side blocklist check and a real strength estimate follow that guidance better than a five-rule checklist.

Try it in your project

Password Input

Pro

A password field with a caret-safe reveal toggle, a Caps Lock warning and, for sign-up, a strength meter, rules checklist and generator.

75 components free, no account needed. Pro from $8 a month.

Open Password Input liveSee pricing
  • Password
  • Forms
  • Authentication
  • Accessibility
  • shadcn/ui

Share

SR

About the author

Serban Rusu

Founder of Wingo UI

Serban Rusu founded Wingo UI. He builds its component library, CLI and MCP server, and writes about React interfaces for phones and AI coding agents.

More from Serban
Keep reading

Related posts

All posts
Component guides

Romanian CUI Validation in TypeScript, Zod and React

Romanian CUI validation in TypeScript: the 753217532 check digit step by step, the RO prefix, a zod rule, a React field and a company lookup at ANAF.

SRSerban Rusu·Oct 9, 2026·11 min read
Build it: screens and apps

Shadcn Login Page: Magic Link, Codes, 2FA and Recovery

Build a shadcn login page in Next.js with password, magic link, email codes, social sign-in, two-step codes and password reset, with every error state wired.

SRSerban Rusu·Oct 16, 2026·11 min read
Component guides

React Country Select With Flags, Search and Dial Codes

Build a React country select with flags, names localized by the browser, search by name, code or +dial code, a suggested group, multiple picks and ISO values.

SRSerban Rusu·Nov 6, 2026·9 min read
Newsletter

Get new posts by email

New posts, product news and occasional offers from Wingo UI. We count clicks to keep it relevant.

No spam. Unsubscribe at any time.

WingoUI

Animated, configurable, mobile-first React components and blocks. Copy the source, make it yours, and let your coding agent build with it.

Product

  • Components
  • Templates
  • Pricing
  • AI agents
  • For teams
  • Theme generator

Resources

  • Docs
  • Contact
  • Programs
  • Blog
  • Changelog
  • Changelog RSS
  • llms.txt

Compare

  • Wingo UI vs paid kits
  • Wingo UI vs shadcn/ui
  • shadcn/ui alternatives
  • MUI alternatives
  • Tailwind Plus alternatives
  • Magic UI alternatives
  • Free components

Legal

  • Terms
  • Privacy
  • Refunds
  • License
  • Imprint

Component categories

  • Buttons & Actions
  • Inputs
  • Forms
  • Navigation
  • Overlays
  • Feedback
  • Data Display
  • Tables & Lists
  • Charts & Stats
  • Layout
  • Media
  • AI Kit
  • Text & Effects
  • Mobile
  • Commerce
  • Marketing Sections
  • Blocks
  • Hooks & Utilities

© 2026 Wingo UI

Wingo UI