Input OTP
Accessible one-time password component with copy paste functionality.
-
import InputOTP from '@dinui/react/input-otp'
export default function InputOTPDemo() { return ( <InputOTP maxLength={6}> <InputOTP.Group> <InputOTP.Slot index={0} /> <InputOTP.Slot index={1} /> <InputOTP.Slot index={2} /> </InputOTP.Group> <InputOTP.Separator /> <InputOTP.Group> <InputOTP.Slot index={3} /> <InputOTP.Slot index={4} /> <InputOTP.Slot index={5} /> </InputOTP.Group> </InputOTP> )}About
Input OTP is built on top of input-otp by @guilherme_rodz.
Pattern
Use the pattern prop to define a custom pattern for the OTP input.
import InputOTP, { REGEXP_ONLY_DIGITS_AND_CHARS } from '@dinui/react/input-otp'
export default function InputOTPPattern() { return ( <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}> <InputOTP.Group> <InputOTP.Slot index={0} /> <InputOTP.Slot index={1} /> <InputOTP.Slot index={2} /> <InputOTP.Slot index={3} /> <InputOTP.Slot index={4} /> <InputOTP.Slot index={5} /> </InputOTP.Group> </InputOTP> )}Separator
-
-
import InputOTP from '@dinui/react/input-otp'
export default function InputOTPWithSeparator() { return ( <InputOTP maxLength={6}> <InputOTP.Group> <InputOTP.Slot index={0} /> <InputOTP.Slot index={1} /> </InputOTP.Group> <InputOTP.Separator /> <InputOTP.Group> <InputOTP.Slot index={2} /> <InputOTP.Slot index={3} /> </InputOTP.Group> <InputOTP.Separator /> <InputOTP.Group> <InputOTP.Slot index={4} /> <InputOTP.Slot index={5} /> </InputOTP.Group> </InputOTP> )}Controlled
You can use the value and onChange props to control the input value.
Enter your one-time password.
'use client'
import InputOTP from '@dinui/react/input-otp'import * as React from 'react'
export default function InputOTPControlled() { const [value, setValue] = React.useState('')
return ( <div className="space-y-2"> <InputOTP maxLength={6} value={value} onChange={(value) => setValue(value)}> <InputOTP.Group> <InputOTP.Slot index={0} /> <InputOTP.Slot index={1} /> <InputOTP.Slot index={2} /> <InputOTP.Slot index={3} /> <InputOTP.Slot index={4} /> <InputOTP.Slot index={5} /> </InputOTP.Group> </InputOTP> <div className="text-center text-sm"> {value === '' ? <>Enter your one-time password.</> : <>You entered: {value}</>} </div> </div> )}Form
'use client'
import Button from '@dinui/react/button'import Form from '@dinui/react/form'import InputOTP from '@dinui/react/input-otp'import { zodResolver } from '@hookform/resolvers/zod'import { useForm } from 'react-hook-form'import { z } from 'zod'
const FormSchema = z.object({ pin: z.string().min(6, { message: 'Your one-time password must be 6 characters.', }),})
export default function InputOTPForm() { const form = useForm<z.infer<typeof FormSchema>>({ resolver: zodResolver(FormSchema), defaultValues: { pin: '', }, })
function onSubmit(data: z.infer<typeof FormSchema>) { alert(`You submitted the following values: ${JSON.stringify(data, null, 2)}`) }
return ( <Form form={form} onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6"> <Form.Field control={form.control} name="pin" render={({ field }) => ( <Form.Item> <Form.Label>One-Time Password</Form.Label> <Form.Control> <InputOTP maxLength={6} {...field}> <InputOTP.Group> <InputOTP.Slot index={0} /> <InputOTP.Slot index={1} /> <InputOTP.Slot index={2} /> <InputOTP.Slot index={3} /> <InputOTP.Slot index={4} /> <InputOTP.Slot index={5} /> </InputOTP.Group> </InputOTP> </Form.Control> <Form.Description> Please enter the one-time password sent to your phone. </Form.Description> <Form.ErrorMessage /> </Form.Item> )} />
<Button type="submit">Submit</Button> </Form> )}Installation
-
Follow Installation Guide
To enable DinUI functionality in your project, you will need to properly set up Tailwind and install the necessary dependencies. -
All done
You now can start using this component in your project.
-
Follow Installation Guide
To enable DinUI functionality in your project, you will need to properly set up Tailwind and install the necessary dependencies. -
Run the following command in your project
Terminal window npx @dinui/cli@latest add input-otpTerminal window yarn dlx @dinui/cli@latest add input-otpTerminal window pnpm dlx @dinui/cli@latest add input-otpTerminal window bunx @dinui/cli@latest add input-otp -
Update the import paths to match your project setup
-
All done
You now can start using this component in your project.
-
Follow Installation Guide
To enable DinUI functionality in your project, you will need to properly set up Tailwind and install the necessary dependencies. -
Install dependencies
Terminal window npm install input-otp tailwind-variants type-fest input-otp input-otpTerminal window yarn add input-otp tailwind-variants type-fest input-otp input-otpTerminal window pnpm add input-otp tailwind-variants type-fest input-otp input-otpTerminal window bun add input-otp tailwind-variants type-fest input-otp input-otp -
Copy and paste the following code into your project
'use client'import { OTPInput, OTPInputContext } from 'input-otp'import type React from 'react'import { forwardRef, useContext } from 'react'import { tv } from 'tailwind-variants'import type { Merge } from 'type-fest'const inputOTP = tv({slots: {root: 'disabled:cursor-not-allowed',container: 'flex items-center gap-2 has-[:disabled]:opacity-50',group: 'flex items-center',slot: ['relative flex size-9 items-center justify-center','text-sm shadow-sm transition-all','border-y border-r first:rounded-l-md first:border-l last:rounded-r-md','data-[active=true]:z-10 data-[active=true]:outline data-[active=true]:outline-2',],fakeCaret: ['pointer-events-none', 'h-4 w-px animate-caret-blink bg-fg duration-1000'],separator: null,},})const InputOTPRoot = forwardRef<React.ElementRef<typeof OTPInput>,React.ComponentPropsWithoutRef<typeof OTPInput>>((props, ref) => {const { root, container } = inputOTP()return (<OTPInput{...props}ref={ref}containerClassName={container({ className: props.containerClassName })}className={root({ className: props.className })}/>)})InputOTPRoot.displayName = 'InputOTP'const InputOTPGroup = forwardRef<React.ElementRef<'div'>, React.ComponentPropsWithoutRef<'div'>>((props, ref) => {const { group } = inputOTP()return <div {...props} ref={ref} className={group({ className: props.className })} />},)InputOTPGroup.displayName = 'InputOTPGroup'const InputOTPSlot = forwardRef<React.ElementRef<'div'>,Merge<React.ComponentPropsWithoutRef<'div'>,{index: numberfakeCaretProps?: React.ComponentProps<'div'>}>>(({ index, fakeCaretProps, ...props }, ref) => {const inputOTPContext = useContext(OTPInputContext)const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] ?? {}const { slot, fakeCaret } = inputOTP()return (<div{...props}ref={ref}data-active={isActive}className={slot({ className: props.className })}>{char}{hasFakeCaret && (<div {...fakeCaretProps} className={fakeCaret({ className: fakeCaretProps?.className })} />)}</div>)})InputOTPSlot.displayName = 'InputOTPSlot'const InputOTPSeparator = forwardRef<React.ElementRef<'div'>,React.ComponentPropsWithoutRef<'div'>>((props, ref) => {const { separator } = inputOTP()return (<divrole="separator"{...props}ref={ref}className={separator({ className: props.className })}>-</div>)})InputOTPSeparator.displayName = 'InputOTPSeparator'const InputOTP = Object.assign(InputOTPRoot, {Slot: InputOTPSlot,Group: InputOTPGroup,Separator: InputOTPSeparator,})export default InputOTPexport { inputOTP }export { REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp'export * as InputOTPPrimitive from 'input-otp' -
Update the import paths to match your project setup
-
All done
You now can start using this component in your project.