Input OTP
optics/input-otpFreeComponent
Accessible one-time password component with copy paste support.
See it running
Open the demo on optics
optics.agusmayol.com.ar/components/input-otpInstall it
npx shadcn@latest add https://optics.agusmayol.com.ar/r/input-otp.jsonSource
1"use client";23import * as React from "react";4import { OTPInput, OTPInputContext } from "input-otp";56import { cn } from "@/lib/utils";7import { MinusIcon } from "lucide-react";89function InputOTP({ className = "", containerClassName = "", ...props }) {10 return (11 <OTPInput12 data-slot="input-otp"13 containerClassName={cn(14 "cn-input-otp flex items-center has-disabled:opacity-50",15 containerClassName,16 )}17 spellCheck={false}18 className={cn("disabled:cursor-not-allowed", className)}19 {...props}20 />21 );22}2324function InputOTPGroup({ className = "", ...props }) {25 return (26 <div27 data-slot="input-otp-group"28 className={cn(29 "has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive rounded-md has-aria-invalid:ring-[2px] flex items-center",30 className,31 )}32 {...props}33 />34 );35}3637function InputOTPSlot({ index, className = "", ...props }) {38 const inputOTPContext = React.useContext(OTPInputContext);39 const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};4041 return (42 <div43 data-slot="input-otp-slot"44 data-active={isActive}45 className={cn(46 "bg-input/20 dark:bg-input/30 border-input data-[active=true]:border-ring data-[active=true]:ring-ring/30 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive size-7 border-y border-r text-xs/relaxed transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:ring-[2px] relative flex items-center justify-center data-[active=true]:z-10 cursor-default",47 className,48 )}49 {...props}50 >51 {char}52 {hasFakeCaret && (53 <div className="pointer-events-none absolute inset-0 flex items-center justify-center">54 <div className="animate-caret-blink duration-1000 bg-foreground h-4 w-px" />55 </div>56 )}57 </div>58 );59}6061function InputOTPSeparator({ ...props } = {}) {62 return (63 <div64 data-slot="input-otp-separator"65 className="[&_svg:not([class*='size-'])]:size-4 flex items-center"66 role="separator"67 {...props}68 >69 <MinusIcon />70 </div>71 );72}7374InputOTP.displayName = "InputOTP";75InputOTPGroup.displayName = "InputOTPGroup";76InputOTPSlot.displayName = "InputOTPSlot";77InputOTPSeparator.displayName = "InputOTPSeparator";7879export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
What it pulls in
npm packages
Other registry items
Files it writes
More from optics
All 79 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | optics | Components | Free | 5 deps | |
| Alertalert | optics | Components | Free | 4 deps | |
| Alert Dialogalert-dialog | optics | Components | Free | 4 deps | |
| Aspect Ratioaspect-ratio | optics | Components | Free | 2 deps | |
| Auto Heightauto-height | optics | Components | Free | 3 deps | |
| Avataravatar | optics | Components | Free | 3 deps | |
| Badgebadge | optics | Components | Free | 4 deps | |
| Breadcrumbbreadcrumb | optics | Components | Free | 4 deps |
