# Input Password

A password field with an accessible visibility toggle.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. An agent skill is available at [/.well-known/agent-skills/site-skill.md](/.well-known/agent-skills/site-skill.md).





## Playground [#playground]

```tsx
"use client";

import { InputPassword } from "@/components/ui/input-password";

export function InputPasswordDemo() {
  return (
    <InputPassword
        label={"Password"}
        placeholder={"Enter password"}
        autoComplete={"current-password"}
        showPasswordLabel={"Show password"}
        hidePasswordLabel={"Hide password"}
    />
  );
}
```

## Installation [#installation]

Initialize shadcn in your React project, then install this component from the Vandor UI registry:

```bash
npx shadcn@latest add https://vandor-ui.vercel.app/r/input-password.json
```

The default installation target is components/ui/input-password.tsx. Review the CLI's overwrite prompt before replacing local customizations.

## Dependencies [#dependencies]

Installed automatically by the registry CLI. For manual setup:

```bash
npm install lucide-react
```

## Usage [#usage]

```tsx
import { InputPassword } from "@/components/ui/input-password";

<InputPassword label="Password" autoComplete="current-password" />;
```

Set `autoComplete="new-password"` for account creation. The default visibility is hidden.

## Examples [#examples]

Choose autocomplete according to each field’s purpose so password managers can help users.

<ComponentPreview name="input-password-demo" title="input-password-demo.tsx">
  <InputPasswordDemo />
</ComponentPreview>

### Optional Storybook Stories [#optional-storybook-stories]

Install Input Password first, then run `npx shadcn@latest add https://vandor-ui.vercel.app/r/input-password-stories.json`. Stories cover visibility toggling, label styles, controlled editing, and disabled/read-only/invalid states. Use fake credentials only; never enter real passwords in Storybook.

Stories install beside Input Password under your configured UI alias and use relative imports. Use your existing matching `@storybook/react` packages (verified with 10.6.1), discover `**/*.stories.tsx`, and load your global Tailwind/theme stylesheet. No addons, extra packages, core overwrites, or Storybook config edits are required. Default-value Controls remount uncontrolled examples; Controlled owns local state. Storybook UI is not verified because no server exists here.

## Behavior & Accessibility [#behavior--accessibility]

The show/hide button has a changing accessible name and does not submit a form. Existing password-manager attributes are forwarded. This component does not enforce password policy or strength.

Localize the action with `showPasswordLabel="Tampilkan kata sandi"` and `hidePasswordLabel="Sembunyikan kata sandi"`. Defaults remain "Show password" and "Hide password". `aria-pressed` reflects visible state; a disabled field disables its visibility action too. The playground Reset remounts the field to restore hidden visibility. Only fake credentials belong in examples.

## Props [#props]

| Prop              | Type                                        | Default         | Description                                                              |
| ----------------- | ------------------------------------------- | --------------- | ------------------------------------------------------------------------ |
| autoComplete      | "current-password" \| "new-password"        | native          | InputPassword: preserves browser password-manager hints.                 |
| defaultValue      | string                                      | undefined       | InputPassword: initial uncontrolled value.                               |
| disabled          | boolean                                     | false           | InputPassword: prevents editing and visibility toggle.                   |
| label             | string                                      | undefined       | InputPassword: visible associated label.                                 |
| onChange          | React.ChangeEventHandler\<HTMLInputElement> | undefined       | InputPassword: native input change callback.                             |
| placeholder       | string                                      | undefined       | InputPassword: native placeholder.                                       |
| readOnly          | boolean                                     | false           | InputPassword: prevents editing but keeps focus and visibility controls. |
| showPasswordLabel | string                                      | "Show password" | Accessible name while the password is hidden.                            |
| hidePasswordLabel | string                                      | "Hide password" | Accessible name while the password is visible.                           |
| value             | string                                      | uncontrolled    | InputPassword: native controlled value.                                  |

The reference lists component-specific props and selected native props, not the complete React API. Other native attributes are forwarded to the rendered element.

## Source [#source]

```tsx
"use client";

import { Eye, EyeOff } from "lucide-react";
import * as React from "react";

import { Button } from "./button";
import { Input } from "./input";
import type { InputProps } from "./input";

export type InputPasswordProps = Omit<InputProps, "type" | "icon"> & {
  showPasswordLabel?: string;
  hidePasswordLabel?: string;
};

export const InputPassword = ({
  className,
  disabled,
  readOnly,
  showPasswordLabel = "Show password",
  hidePasswordLabel = "Hide password",
  ...props
}: InputPasswordProps) => {
  const [visible, setVisible] = React.useState(false);
  return (
    <div data-slot="password-input" className="relative min-w-0">
      <Input
        {...props}
        type={visible ? "text" : "password"}
        disabled={disabled}
        readOnly={readOnly}
        className={["pr-11", className].filter(Boolean).join(" ")}
      />
      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        disabled={disabled}
        aria-label={visible ? hidePasswordLabel : showPasswordLabel}
        aria-pressed={visible}
        className="absolute top-1/2 right-1 -translate-y-1/2"
        onClick={() => setVisible((current) => !current)}
      >
        {visible ? <EyeOff aria-hidden="true" /> : <Eye aria-hidden="true" />}
      </Button>
    </div>
  );
};

```
