Skip to main content
Loading...

Features

  • Includes button to toggle visibility of the password
  • Automatic focus restoration to the input
  • Resets visibility to hidden after form submission
  • Can ignore supported password managers

Installation

Install the password-input package:

npm install @zag-js/password-input @zag-js/react # or yarn add @zag-js/password-input @zag-js/react

Anatomy

Check the password-input anatomy and part names.

Each part includes a data-part attribute to help identify them in the DOM.

Usage

Import the password-input package:

import * as passwordInput from "@zag-js/password-input"

The password-input package exports two key functions:

  • machine - State machine logic.
  • connect - Maps machine state to JSX props and event handlers.

Pass a unique id to useMachine so generated element ids stay predictable.

Then use the framework integration helpers:

import * as passwordInput from "@zag-js/password-input" import { useMachine, normalizeProps } from "@zag-js/react" import { EyeIcon, EyeOffIcon } from "lucide-react" import { useId } from "react" function PasswordInput() { const service = useMachine(passwordInput.machine, { id: useId() }) const api = passwordInput.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> <label {...api.getLabelProps()}>Password</label> <div {...api.getControlProps()}> <input {...api.getInputProps()} /> <button {...api.getVisibilityTriggerProps()}> <span {...api.getIndicatorProps()}> {api.visible ? <EyeIcon /> : <EyeOffIcon />} </span> </button> </div> </div> ) }

Setting the initial visibility

Set defaultVisible to define the initial visibility.

const service = useMachine(passwordInput.machine, { id: useId(), defaultVisible: true, })

Controlling the visibility

Use visible and onVisibilityChange to control visibility externally.

const service = useMachine(passwordInput.machine, { id: useId(), visible: true, onVisibilityChange(details) { // details => { visible: boolean } console.log(details.visible) }, })

Ignoring password managers

Set ignorePasswordManagers to true to ignore supported password managers.

This is useful when you want to ensure that the password input is not managed by password managers. Currently, this only works for 1Password, LastPass, Bitwarden, Dashlane, and Proton Pass.

const service = useMachine(passwordInput.machine, { id: useId(), ignorePasswordManagers: true, })

Why is this useful?

  • You might want to use this primitive for non-login scenarios (e.g., "secure notes", "temporary passwords")

  • In a verify password step, you might want to disable password managers for the confirm password field to ensure manual entry

  • Building a security-sensitive app where password managers violate compliance requirements.

Managing autocompletion

Set autoComplete to control password autofill behavior.

  • new-password — The user is creating a new password.
  • current-password — The user is entering an existing password.
const service = useMachine(passwordInput.machine, { id: useId(), autoComplete: "new-password", })

Making the input required

Set required to true to make the input required.

const service = useMachine(passwordInput.machine, { id: useId(), required: true, })

Making the input read only

Set readOnly to true to make the input read only.

const service = useMachine(passwordInput.machine, { id: useId(), readOnly: true, })

Setting the input name

Set name to include the password field in form submission.

const service = useMachine(passwordInput.machine, { id: useId(), name: "password", })

Customizing accessibility labels

Use translations.visibilityTrigger to customize the toggle button label.

const service = useMachine(passwordInput.machine, { id: useId(), translations: { visibilityTrigger: (visible) => visible ? "Hide password" : "Show password", }, })

Styling guide

Each part includes a data-part attribute you can target in CSS.

[data-scope="password-input"][data-part="root"] { /* styles for the root part */ } [data-scope="password-input"][data-part="input"] { /* styles for the input part */ } [data-scope="password-input"][data-part="visibility-trigger"] { /* styles for the visibility trigger part */ } [data-scope="password-input"][data-part="indicator"] { /* styles for the indicator part */ } [data-scope="password-input"][data-part="control"] { /* styles for the control part */ } [data-scope="password-input"][data-part="label"] { /* styles for the label part */ }

Visibility State

Use the [data-state="visible"] and [data-state="hidden"] attributes to style the password input when it is visible or hidden.

[data-scope="password-input"][data-part="input"][data-state="visible"] { /* styles for the visible state (for input) */ } [data-scope="password-input"][data-part="visibility-trigger"][data-state="visible"] { /* styles for the visible state (for visibility trigger) */ }

Disabled State

Use the [data-disabled] attribute to style the password input when it is disabled.

[data-scope="password-input"][data-part="input"][data-disabled] { /* styles for the disabled state */ }

Invalid State

Use the [data-invalid] attribute to style the password input when it is invalid.

[data-scope="password-input"][data-part="input"][data-invalid] { /* styles for the invalid state */ }

Readonly State

Use the [data-readonly] attribute to style the password input when it is read only.

[data-scope="password-input"][data-part="input"][data-readonly] { /* styles for the readonly state */ }

Methods and Properties

Machine Context

The password-input machine exposes the following context properties:

  • defaultVisiblebooleanThe default visibility of the password input.
  • visiblebooleanWhether the password input is visible.
  • onVisibilityChange(details: VisibilityChangeDetails) => voidFunction called when the visibility changes.
  • idsPartial<{ input: string; visibilityTrigger: string; }>The ids of the password input parts
  • disabledbooleanWhether the password input is disabled.
  • invalidbooleanThe invalid state of the password input.
  • readOnlybooleanWhether the password input is read only.
  • requiredbooleanWhether the password input is required.
  • translationsPartial<{ visibilityTrigger: (visible: boolean) => string; }>The localized messages to use.
  • ignorePasswordManagersbooleanWhen `true`, the input will ignore password managers. **Only works for the following password managers** - 1Password, LastPass, Bitwarden, Dashlane, Proton Pass
  • autoComplete"current-password" | "new-password"The autocomplete attribute for the password input.
  • namestringThe name of the password input.
  • dir"ltr" | "rtl"The document's text/writing direction.
  • idstringThe unique identifier of the machine.
  • getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

Machine API

The password-input api exposes the following methods:

  • visiblebooleanWhether the password input is visible.
  • disabledbooleanWhether the password input is disabled.
  • invalidbooleanWhether the password input is invalid.
  • focusVoidFunctionFocus the password input.
  • setVisible(value: boolean) => voidSet the visibility of the password input.
  • toggleVisibleVoidFunctionToggle the visibility of the password input.
Edit this page on GitHub