Input OTP
Accessible one-time password component with copy paste functionality.
About
Input OTP is built on top of input-otp by @guilherme_rodz.
Installation
Install the following dependencies:
npm install input-otpCopy and paste the following code into your project.
'use client';
import clsx from 'clsx';
import { OTPInput, OTPInputContext } from 'input-otp';
import { Dot } from 'lucide-react';
import { ComponentPropsWithoutRef, ElementRef, forwardRef, useContext } from 'react';
import styles from './styles.module.scss';
const InputOTP = forwardRef<ElementRef<typeof OTPInput>, ComponentPropsWithoutRef<typeof OTPInput>>(
({ className, containerClassName, ...props }, ref) => (
<OTPInput
ref={ref}
containerClassName={clsx(styles.otp__input__container, containerClassName)}
className={clsx(styles.otp__input, className)}
{...props}
/>
)
);
InputOTP.displayName = 'InputOTP';
const InputOTPGroup = forwardRef<ElementRef<'div'>, ComponentPropsWithoutRef<'div'>>(({ className, ...props }, ref) => (
<div ref={ref} className={clsx(styles.otp__group, className)} {...props} />
));
InputOTPGroup.displayName = 'InputOTPGroup';
const InputOTPSlot = forwardRef<ElementRef<'div'>, ComponentPropsWithoutRef<'div'> & { index: number }>(
({ index, className, ...props }, ref) => {
const inputOTPContext = useContext(OTPInputContext);
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
return (
<div
ref={ref}
className={clsx(styles.otp__slot, isActive && styles.otp__slot__active, className)}
{...props}
>
{char}
{hasFakeCaret && (
<div className={styles.otp__slot__caret}>
<div />
</div>
)}
</div>
);
}
);
InputOTPSlot.displayName = 'InputOTPSlot';
const InputOTPSeparator = forwardRef<ElementRef<'div'>, ComponentPropsWithoutRef<'div'>>(({ ...props }, ref) => (
<div ref={ref} role='separator' {...props}>
<Dot />
</div>
));
InputOTPSeparator.displayName = 'InputOTPSeparator';
export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };Update the import paths to match your project setup.
Usage
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@/components/ui/input-otp';<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>Examples
Pattern
Use the pattern prop to define a custom pattern for the OTP input.
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
...
<InputOTP
maxLength={6}
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
>
<InputOTPGroup>
<InputOTPSlot index={0} />
{/* ... */}
</InputOTPGroup>
</InputOTP>Separator
You can use the <InputOTPSeparator /> component to add a separator between the input groups.
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"
...
<InputOTP maxLength={4}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
</InputOTPGroup>
</InputOTP>Controlled
You can use the value and onChange props to control the input value.
Enter your one-time password.