Components
Input OTP
Accessible one-time password component with copy paste functionality.
1
2
3
4
5
6
Enter the code sent to your phone.
Installation#
Install the dependencies
npm install cn input-otp lucide-reactCopy the source into your project
components/ui/input-otp.tsx"use client"import * as React from "react"import { cn } from "cn"import { OTPInput, OTPInputContext } from "input-otp"import { MinusIcon } from "lucide-react"function InputOTP({ className, containerClassName, ...props}: React.ComponentProps<typeof OTPInput> & { containerClassName?: string}) { return ( <OTPInput data-slot="input-otp" containerClassName={cn( "cn-input-otp flex items-center has-disabled:opacity-50", containerClassName )} spellCheck={false} className={cn("disabled:cursor-not-allowed", className)} {...props} /> )}function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="input-otp-group" className={cn( "flex items-center rounded-lg has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40", className )} {...props} /> )}function InputOTPSlot({ index, className, ...props}: React.ComponentProps<"div"> & { index: number}) { const inputOTPContext = React.useContext(OTPInputContext) const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {} return ( <div data-slot="input-otp-slot" data-active={isActive} className={cn( "relative flex size-8 items-center justify-center border-y border-r border-input text-sm transition-all outline-none first:rounded-l-lg first:border-l last:rounded-r-lg aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-3 data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40", className )} {...props} > {char} {hasFakeCaret && ( <div className="pointer-events-none absolute inset-0 flex items-center justify-center"> <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" /> </div> )} </div> )}function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) { return ( <div data-slot="input-otp-separator" className="flex items-center [&_svg:not([class*='size-'])]:size-4" role="separator" {...props} > <MinusIcon /> </div> )}export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }Make sure the foundation is in place
Components read their colors, radius and surfaces from the tokens in
app/globals.css. See Theming.
Usage#
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator,} from "@/components/ui/input-otp"Examples#
Alphanumeric
Controlled
Enter your one-time password.
Disabled
1
2
3
4
5
6
Form
Four Digits
Invalid
0
0
0
0
0
0