PasswordInput
FormLabeled password entry with an accessible visibility toggle.
Responsive viewport — resize the window to explore. Direction and density apply only to this preview. Theme applies to the docs too.
"use client";
import { PasswordInput } from "@/components/ui/password-input";
export default function PasswordInputExample() {
return (
<div className="w-full max-w-sm space-y-6">
<div>
<h3 className="mb-1 font-semibold">Protect your workspace</h3>
<p className="text-sm text-muted-foreground">
Use a unique password you do not use elsewhere.
</p>
</div>
<PasswordInput
label="New password"
autoComplete="new-password"
placeholder="Enter a new password"
/>
<PasswordInput
label="Existing password"
autoComplete="current-password"
defaultValue="Sample-only-2026"
/>
<PasswordInput
label="Managed by your organization"
disabled
value="managed-password"
readOnly
/>
<p className="text-xs text-muted-foreground">
The visibility button never submits your form.
</p>
</div>
);
}Usage
Use this input for current or new passwords, with an appropriate autoComplete value. It forwards ordinary Input props, including name, required, disabled, value, defaultValue, and onChange. Visibility is local state; changing it preserves the input value. Place any validation or policy guidance nearby.
import { PasswordInput } from "@/components/ui/password-input";Shipped as editable source in src/components/ui/password-input.tsx in both apps. Copy the implementation below when moving it to another project. Copy the complete example from Source code for all imports and state. Add "use client" when using hooks or event handlers. See installation and theming for project setup.
Component source
The complete implementation is included in both apps. It uses the React, Radix, Lucide, and shared-core dependencies already installed in Apex. Keep your project’s semantic theme tokens when reusing it.
src/components/ui/password-input.tsx
"use client";
import { useId, useState, type ComponentProps } from "react";
import { Eye, EyeOff } from "lucide-react";
import { Input } from "@dashboardpack/core/components/ui/input";
import { Label } from "@dashboardpack/core/components/ui/label";
import { cn } from "@dashboardpack/core/lib/utils";
export interface PasswordInputProps
extends Omit<ComponentProps<typeof Input>, "type"> {
label: string;
}
export function PasswordInput({
label,
id,
className,
disabled,
...props
}: PasswordInputProps) {
const generatedId = useId();
const inputId = id ?? generatedId;
const [visible, setVisible] = useState(false);
return (
<div className={cn("space-y-2", className)}>
<Label htmlFor={inputId}>{label}</Label>
<div className="relative">
<Input
{...props}
id={inputId}
disabled={disabled}
type={visible ? "text" : "password"}
className="pe-12"
/>
<button
type="button"
disabled={disabled}
aria-label={`${visible ? "Hide" : "Show"} ${label}`}
aria-pressed={visible}
aria-controls={inputId}
onClick={() => setVisible(!visible)}
className="absolute inset-y-0 end-0 flex w-11 items-center justify-center rounded-e-md text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50"
>
{visible ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
</button>
</div>
</div>
);
}API reference
Common props and defaults. Slot-specific props are named below; native elements also accept their standard HTML attributes. The component TypeScript definitions are the complete type reference.
| Prop | Type / Default | Description |
|---|---|---|
| label | stringDefault: required | Visible field label and the basis of the toggle accessible name. |
| id | stringDefault: generated | Override the generated input id for external help and validation links. |
| autoComplete | stringDefault: — | Use current-password or new-password to assist password managers. |
| disabled | booleanDefault: false | Disable the field and visibility control. |
| value / defaultValue | Input valueDefault: — | Use controlled value plus onChange, or uncontrolled defaultValue. |
| onChange | ChangeEventHandler<HTMLInputElement>Default: — | Receive edits using the ordinary input event. |
| className | stringDefault: — | Extend the component container with theme-aware utility classes. |