Password input

Inspect

A password field is a compound, the way a Base UI component is. PasswordField is the root: it owns whether the value is revealed and renders no element of its own. PasswordInput is the control and PasswordToggle the eye; both read the root, so the control's type and the eye's accessible name always agree.

Which dialect a screen gets is decided by what you compose around them, never by a prop:

  • The join screens wrap the parts in a floating field: PasswordInput render={<FloatingFieldControl />} hands its type to the floating control, and the toggle rides in the field's trailing slot.
  • Login, and anywhere the password is checked rather than chosen, puts the control in an input group with the lock icon leading and the toggle trailing, on a screen where nothing floats.

It deliberately does not render a strength meter. The tucked error card requires FieldError to be the group's adjacent sibling, so anything the field wrapped around itself would wedge between the two and detach the error into a floating pill. Where a password is being chosen, compose PasswordStrength after the error line.

Examples

Login

Signing in, changing an email, confirming a destructive action. Lock icon, the dictionary's placeholder, reveal toggle.

Inspect

Floating label

The join-flow dialect. appearance="white" is the join screens' white card on the auth shell's grey.

Inspect

Strength

Compose PasswordStrength where a password is being chosen — signup, reset, change. Five rungs, one per requirement, filled to the score, because a member deciding whether to keep typing needs to see how far they have to go: a single "weak" says the password is bad but not what would fix it. The requirements are the meter — each ticks green the moment it is satisfied.

Order matters: the field, then its FieldError, then the meter — never the meter between the two.

<Field data-invalid={invalid}>
  <PasswordField>
    <FloatingField id="signup-password" trailing={<PasswordToggle />}>
      <PasswordInput
        render={<FloatingFieldControl autoComplete="new-password" {...field} />}
      />
      <FloatingFieldLabel>Password</FloatingFieldLabel>
    </FloatingField>
  </PasswordField>
  {invalid && <FieldError errors={[error]} />}
  <PasswordStrength value={password} />
</Field>
Inspect

Invalid and disabled

The same states in both dialects. aria-invalid on the control paints the halo and turns a floating label destructive; the message belongs in a FieldError, not in the meter — the meter reports strength, never form validity.

Inspect

Scoring

checkStrength lives beside the component in password-input.utils.ts and ticks off five requirements: the schema's minimum length (PASSWORD_MIN_LENGTH, 8), a number, a lowercase letter, an uppercase letter, a symbol. The verdict is the count: none met is empty, up to two is weak, up to four is medium, all five is strong.

It is a hint, not the gate. The gate is the schema's minimum length and the auth provider's own policy behind it. Import PASSWORD_MIN_LENGTH from the same file when writing the validation schema, so the gate and the meter cannot drift apart.

Notes

  • The meter takes the current value — wire it to the same state or watch the form field, so it never depends on how the caller wired the control.
  • The verdict is aria-live="polite", so a screen reader hears it change without the value being read back on every keystroke.
  • The bar is a Base UI Meter (role meter, aria-valuenow is the score, the value text the "n/5 requirements met" line); each requirement carries a visually hidden "met" / "not met".
  • State lives on the DOM: data-visible on the control and the toggle while the value is revealed; data-strength="empty | weak | medium | strong" on the meter, data-score on its bar, data-state="filled | empty" on each rung and "met | unmet" on each requirement.
  • Set autoComplete="new-password" when choosing and "current-password" when signing in, so password managers offer the right thing.

API

PasswordField:

PropTypeDescription
defaultVisiblebooleanStart revealed. Rarely right: a password is hidden until asked.

PasswordInput — InputGroupInput's props, plus:

PropTypeDescription
renderReactElementThe control to render instead of an InputGroupInput — a FloatingFieldControl inside a FloatingField. It receives the type the root decides.

PasswordToggle takes InputGroupButton's props; its aria-label, onClick and icon are the root's.

PasswordStrength:

PropTypeDescription
valuestringThe password being judged. Empty renders a silent meter.
classNamestringExtra classes on the meter's wrapper.