Logo
Components

Password Input

A specialized input component for passwords with built-in visibility toggling, customizable icons, and full accessibility support.

Installation

Run the following command to add the password-input component to your project using the Flow UI CLI:

npx theflowui add password-input

Install Dependencies

Ensure you have lucide-react installed for the default icons.

npm install lucide-react

Create Password Input Component

Create a new file password-input.tsx in your components folder (e.g., components/ui/password-input.tsx) and copy the code below.

components/ui/password-input.tsx
"use client";

import { cn } from "@/lib/utils";
import { Eye, EyeOff } from "lucide-react";
import type { LucideIcon } from "lucide-react";
import { ComponentPropsWithoutRef, forwardRef, useState } from "react";

type PasswordInputProps = {
    className?: string;
    containerClassName?: string;
    visibleIcon?: LucideIcon;
    hiddenIcon?: LucideIcon;
} & ComponentPropsWithoutRef<"input">;

const PasswordInput = forwardRef<
    HTMLInputElement,
    PasswordInputProps
>(
    ({
        className,
        containerClassName,
        disabled,
        visibleIcon = Eye,
        hiddenIcon = EyeOff,
        ...props
    },
        ref
    ) => {
        // State to handle password visibility
        const [visible, setVisible] = useState<boolean>(false);

        // Icon selection based on the visibility state
        const Icon = visible ? visibleIcon : hiddenIcon;

        // Function to handle the visibility toggle
        const handleVisibility = () => {
            if (!disabled) {
                setVisible((prev) => !prev);
            }
        }
        return (
            <div
                className={cn(
                    "relative flex items-center border border-input bg-background rounded-md ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
                    containerClassName
                )}
            >
                <input
                    ref={ref}
                    type={visible ? "text" : "password"}
                    disabled={disabled}
                    className={cn(
                        "flex rounded-md px-3 py-1.5 pe-10 disabled:cursor-not-allowed disabled:opacity-50 outline-none w-full",
                        className
                    )}
                    {...props}
                />
                <button
                    type="button"
                    onClick={handleVisibility}
                    disabled={disabled}
                    aria-label={visible ? "Hide password" : "Show password"}
                    className={cn(
                        "absolute right-0 me-3 inline-flex justify-center items-center text-muted-foreground hover:text-foreground focus:outline-none disabled:opacity-50"
                    )}
                >
                    <Icon
                        className="size-5"
                    />
                </button>
            </div>
        )
    }
);

PasswordInput.displayName = "PasswordInput";

export { PasswordInput };

Usage

Custom Icons

You can customize the icons used for toggling visibility by passing components to visibleIcon and hiddenIcon props.

import { PasswordInput } from "@/components/ui/password-input";
import { Lock, Unlock } from "lucide-react";

export function CustomIconsExample() {
    return (
        <PasswordInput 
            visibleIcon={Unlock} 
            hiddenIcon={Lock} 
            placeholder="Custom lock icons..."
        />
    );
}

Custom Styling

You can customize the outer container, i.e. give border and outline on focus using focus-within tailwind class. You can also style the Icon, so show your creativity.

import { PasswordInput } from "@/components/ui/password-input";
import { Lock, Unlock } from "lucide-react";

export const CustomizedExample = () => {
    return (
        <PasswordInput
            containerClassName="focus-within:ring-2 focus-within:ring-accent"
            placeholder="password"
        />
    );
}

Disabled State

The component handles the disabled state by preventing interaction and applying appropriate styles.

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

<PasswordInput disabled value="secretpassword" />

Props

The PasswordInput component accepts all native <input> HTML attributes, plus the following:

PropTypeDefaultDescription
classNamestring-Additional CSS classes for the input element.
containerClassNamestring-Additional CSS classes for the wrapper container.
iconClassNamestring-Additional CSS classes for the icon.
visibleIconLucideIconEyeIcon to show when the password is visible.
hiddenIconLucideIconEyeOffIcon to show when the password is hidden.

Accessibility

The component automatically handles ARIA labels for the visibility toggle button and uses type="password" or type="text" appropriately to ensure password managers and screen readers behave correctly.

On this page