Password Toggle Field
Primitive

Password Toggle Field

Password input with controlled visibility state and a keyboard-accessible show/hide toggle.

Live behavior

Password Toggle Field in motion

Interactive
Atom behavior · App-owned appearance
Preparing behavior…

Interact with the specimen and inspect the behavior Atom contributes.

waiting for input

When to Use

Use PasswordToggleField when a password input needs a button that lets people check what they typed. Use Input for ordinary text and OTPField for a short one-time verification code. Only add the visibility toggle when revealing the password is acceptable for the product's security and privacy needs.

Features

The behavior Atom owns before your product adds appearance.

  • Controlled and uncontrolled password visibility.
  • Native password input type switching.
  • Toggle button with dynamic accessible label.
  • Optional icon part that switches visible/hidden content.
  • Disabled, read-only, required, and invalid state propagation.
  • Headless only: no icon, layout, or visual affordance is included.

Import

tsx
import { PasswordToggleField } from "@flowstack-ui/atom";

Anatomy

tsx
<PasswordToggleField.Root>
  <PasswordToggleField.Input />
  <PasswordToggleField.Toggle>
    <PasswordToggleField.Icon />
  </PasswordToggleField.Toggle>
</PasswordToggleField.Root>

API Reference

Root

Provides visibility and field state to its compound parts without rendering a DOM element.

PropTypeDefault
childrenReactNoderequired
visibleboolean-
defaultVisiblebooleanfalse
onVisibleChange(visible: boolean) => void-
disabledbooleanfalse
readOnlybooleanfalse
requiredbooleanfalse
invalidbooleanfalse
validationBehavior"inline" | "native"Field/Form value or "native"
showLabelstring"Show password"
hideLabelstring"Hide password"

Input

Renders the native input and changes its owned type between password and text as visibility changes. Other native input props pass through.

PropTypeDefault
childrenReactNode-
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-invalidPresent when invalid
aria-readonlyPresent when read only
aria-requiredPresent when required
Data attributeValues
[data-slot]"password-toggle-field-input"
[data-state]"visible" | "hidden"
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-required]Present when required
[data-invalid]Present when invalid

Toggle

Renders the button that changes visibility. Pointer down is prevented so clicking it does not move focus away from the password input.

PropTypeDefault
childrenReactNode-
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-label"Show password" or "Hide password" by default
Data attributeValues
[data-slot]"password-toggle-field-toggle"
[data-state]"visible" | "hidden"
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-required]Present when required
[data-invalid]Present when invalid

Icon

Decorative icon slot that renders visible or hidden content.

PropTypeDefault
visibleReactNode-
hiddenReactNode-
ARIA attributeValues
aria-hiddentrue
Data attributeValues
[data-slot]"password-toggle-field-icon"
[data-state]"visible" | "hidden"

Advanced compound parts can use usePasswordToggleFieldContext; its provider and context value type are also public exports.

Examples

Basic Password Field

tsx
import { PasswordToggleField } from "@flowstack-ui/atom";

export function PasswordField() {
  return (
    <PasswordToggleField.Root>
      <PasswordToggleField.Input
        aria-label="Password"
        name="password"
        autoComplete="current-password"
      />
      <PasswordToggleField.Toggle>
        <PasswordToggleField.Icon visible="Hide" hidden="Show" />
      </PasswordToggleField.Toggle>
    </PasswordToggleField.Root>
  );
}

Controlled Visibility

tsx
import { useState } from "react";
import { PasswordToggleField } from "@flowstack-ui/atom";

export function ControlledPasswordField() {
  const [visible, setVisible] = useState(false);

  return (
    <PasswordToggleField.Root visible={visible} onVisibleChange={setVisible}>
      <PasswordToggleField.Input aria-label="Password" />
      <PasswordToggleField.Toggle />
    </PasswordToggleField.Root>
  );
}

Accessibility

When nested in Field.Root, Input inherits the generated control ID plus the label, description, and error relationships. Explicit input relationship props continue to take precedence.

The visible password input owns native validity. After a validation attempt, Root coordinates its invalid state across Input and Toggle. Inline behavior suppresses the browser bubble while keeping constraint validation active.

The toggle remains keyboard reachable. Its accessible label changes between “Show password” and “Hide password”; aria-pressed is intentionally not used because the label already communicates the action.

Root's showLabel and hideLabel localize those state-aware actions. Native form reset restores uncontrolled visibility to defaultVisible. Native form submission restores the input element to type="password" before submission. The changing action label communicates the state change; Atom does not add a separate live-region announcement.

KeyDescription
TabMoves focus between the input, toggle, and surrounding controls.
EnterActivates the focused toggle.
SpaceActivates the focused toggle.

Changelog

Unreleased

0.24.0

  • Added public Agent Knowledge for component selection, required composition, recurring mistakes, and validation.

0.19.1

  • Fixed Input to inherit the containing Field's generated control ID, accessible label, description, and error relationships.

0.19.0

  • Added state-aware showLabel and hideLabel localization, form-reset visibility restoration, and password-type restoration before submission.

0.6.16

  • Explicitly scrolled inline validation-directed focus into view.

0.6.15

  • Exposed inline validation-directed focus through [data-focus-visible] until blur.

0.6.13

  • Added inline/native validation presentation and synchronized invalid state across Root, Input, Toggle, Field, and Form.

0.1.0

  • Initial Atom release.