Skip to content

Instantly share code, notes, and snippets.

@mjbalcueva
Last active May 17, 2024 05:13
Show Gist options
  • Save mjbalcueva/b21f39a8787e558d4c536bf68e267398 to your computer and use it in GitHub Desktop.
Save mjbalcueva/b21f39a8787e558d4c536bf68e267398 to your computer and use it in GitHub Desktop.
shadcn ui custom password input
"use client"
import { forwardRef, useState } from "react"
import { EyeIcon, EyeOffIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input, InputProps } from "@/components/ui/input"
import { cn } from "@/lib/utils"
const PasswordInput = forwardRef<HTMLInputElement, InputProps>(
({ className, ...props }, ref) => {
const [showPassword, setShowPassword] = useState(false)
const disabled = props.value === "" || props.value === undefined || props.disabled
return (
<div className="relative">
<Input
type={showPassword ? "text" : "password"}
className={cn("hide-password-toggle pr-10", className)}
ref={ref}
{...props}
/>
<Button
type="button"
variant="ghost"
size="sm"
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
onClick={() => setShowPassword((prev) => !prev)}
disabled={disabled}
>
{showPassword && !disabled ? (
<EyeIcon
className="h-4 w-4"
aria-hidden="true"
/>
) : (
<EyeOffIcon
className="h-4 w-4"
aria-hidden="true"
/>
)}
<span className="sr-only">
{showPassword ? "Hide password" : "Show password"}
</span>
</Button>
{/* hides browsers password toggles */}
<style>{`
.hide-password-toggle::-ms-reveal,
.hide-password-toggle::-ms-clear {
visibility: hidden;
pointer-events: none;
display: none;
}
`}</style>
</div>
)
},
)
PasswordInput.displayName = "PasswordInput"
export { PasswordInput }
"use client"
import { useState } from "react"
import { PasswordInput } from "@/components/password-input"
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
const SampleUseCase = () => {
const [currentPassword, setCurrentPassword] = useState("")
const [password, setPassword] = useState("")
const [passwordConfirmation, setPasswordConfirmation] = useState("")
return (
<div className="space-y-4">
<div>
<Label htmlFor="current_password">Current Password</Label>
<PasswordInput
id="current_password"
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
autoComplete="current-password"
/>
</div>
<div>
<Label htmlFor="password">New Password</Label>
<PasswordInput
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete="new-password"
/>
</div>
<div>
<Label htmlFor="password_confirmation">Confirm Password</Label>
<PasswordInput
id="password_confirmation"
value={passwordConfirmation}
onChange={(e) => setPasswordConfirmation(e.target.value)}
autoComplete="new-password"
/>
</div>
<Button type="submit">Save</Button>
</div>
)
}
export default SampleUseCase
@ProDanish203
Copy link

thanks alot

@laryhills
Copy link

Thanks a lot, made a shadcn-svelte version. -> https://gist.github.com/laryhills/dddd2d17dd0db9179f5686c6afbc9c94

@immdraselkhan
Copy link

immdraselkhan commented Apr 30, 2024

import { EyeIcon, EyeOffIcon } from "lucide-react";
import { useFormContext } from "react-hook-form";
import { Box } from "@/components/ui/box";
import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { createElement, useState } from "react";

type PasswordFieldProps = {
  description?: string | JSX.Element;
};

export function PasswordField({ description }: PasswordFieldProps) {
  const { control, getFieldState } = useFormContext();
  const [passwordVisibility, setPasswordVisibility] = useState(false);

  return (
    <FormField
      control={control}
      name="password"
      render={({ field }) => (
        <FormItem>
          <FormControl>
            <Box className="relative">
              <Input
                type={passwordVisibility ? "text" : "password"}
                placeholder="Enter password"
                autoComplete="on"
                {...field}
                className={`pr-12 ${getFieldState("password").error && "border-red-500"}`}
              />
              <Box
                className="absolute inset-y-0 right-0 flex cursor-pointer items-center p-3 text-muted-foreground"
                onClick={() => setPasswordVisibility(!passwordVisibility)}
              >
                {createElement(passwordVisibility ? EyeOffIcon : EyeIcon, {
                  className: "h-6 w-6",
                })}
              </Box>
            </Box>
          </FormControl>
          <FormMessage />
          {description && <FormDescription>{description}</FormDescription>}
        </FormItem>
      )}
    />
  );
}

This is another approach with better control. Make sure to wrap the form using FormProvider.

Usage example:

<PasswordField
  // description={<Link href="reset">Forgot your password?</Link>}
  description={"Forgot your password?"}
/>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment