Sabil UI

Search documentation

Search for a command to run...

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#

  1. Install the dependencies

    npm install cn input-otp lucide-react
  2. Copy 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 }
  3. 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

Verify your login
Enter the verification code we sent to your email address: m@example.com.

I no longer have access to this email address.

Having trouble signing in? Contact support

Four Digits

Invalid

0
0
0
0
0
0

Pattern

Separator