Input OTP

Accessible one-time password component with copy paste functionality. Perfect for phone verification, 2FA codes, and PIN inputs.

Basic 6-digit OTP

Standard OTP input with 6 digits

Value: ""

Style Variants

Different visual styles for various use cases

Default

Clean border with subtle background

Outline

Bold borders with transparent background

Filled

Subtle filled background, becomes white on focus

Size Variants

Different sizes for different contexts

Small (40×40px)

Medium - Default (48×48px)

Large (56×56px)

With Separators

Group digits with custom separators

Dash Separator

Value: ""

Dot Separator

4-digit PIN

Shorter OTP for PIN codes

Value: ""

Phone Number Verification

Complete verification flow example

Enter the 6-digit code sent to +1 (555) 000-0000

-

Value: ""

Masked Input

Hide entered digits for security

Actual value: "" (displayed as dots)

Auto Focus

Automatically focus first input on mount

Value: ""

Disabled State

Non-interactive state for display purposes

Disabled with preset value

✨ Interactive Features

  • Auto-advance: Automatically moves to next input when typing
  • Backspace: Moves to previous input and clears digit
  • Arrow keys: Navigate between inputs
  • Paste support: Paste 6-digit codes from clipboard
  • Mobile optimized: Shows numeric keyboard on mobile devices
  • Validation: Only accepts numeric input

Installation

No external dependencies required. This component is built using only Tailwind CSS and React.

Usage

tsx
1import InputOTP from '@/app/ui/molecules/InputOTP';
2import { useState } from 'react';
3
4export default function InputOTPExample() {
5 const [value, setValue] = useState('');
6
7 return (
8 <InputOTP
9 length={6}
10 value={value}
11 onValueChange={setValue}
12 />
13 );
14}

API Reference

InputOTP Props

tsx
1interface InputOTPProps {
2 length?: number;
3 value?: string;
4 defaultValue?: string;
5 disabled?: boolean;
6 autoFocus?: boolean;
7 mask?: boolean;
8 separator?: React.ReactNode;
9 separatorIndexes?: number[];
10 size?: 'sm' | 'md' | 'lg';
11 variant?: 'default' | 'outline' | 'filled';
12 onValueChange?: (value: string) => void;
13 onComplete?: (value: string) => void;
14 className?: string;
15}

Examples

Basic Usage

tsx
1function BasicInputOTP() {
2 const [value, setValue] = useState('');
3
4 return (
5 <InputOTP
6 length={6}
7 value={value}
8 onValueChange={setValue}
9 />
10 );
11}

With Separator

tsx
1function WithSeparator() {
2 const [value, setValue] = useState('');
3
4 return (
5 <InputOTP
6 length={6}
7 value={value}
8 onValueChange={setValue}
9 separator={<span className="text-neutral-400">-</span>}
10 separatorIndexes={[2]}
11 />
12 );
13}

4-Digit PIN

tsx
1function FourDigitPIN() {
2 const [value, setValue] = useState('');
3
4 return (
5 <InputOTP
6 length={4}
7 value={value}
8 onValueChange={setValue}
9 />
10 );
11}

Masked Input

tsx
1function MaskedInput() {
2 const [value, setValue] = useState('');
3
4 return (
5 <InputOTP
6 length={4}
7 value={value}
8 onValueChange={setValue}
9 mask={true}
10 />
11 );
12}

Size Variants

tsx
1function SizeExamples() {
2 const [value, setValue] = useState('');
3
4 return (
5 <div className="space-y-4">
6 <InputOTP length={4} size="sm" />
7 <InputOTP length={4} size="md" />
8 <InputOTP length={4} size="lg" />
9 </div>
10 );
11}

Style Variants

tsx
1function VariantExamples() {
2 const [value, setValue] = useState('');
3
4 return (
5 <div className="space-y-4">
6 <InputOTP length={4} variant="default" />
7 <InputOTP length={4} variant="outline" />
8 <InputOTP length={4} variant="filled" />
9 </div>
10 );
11}

Phone Number Verification

tsx
1function PhoneVerification() {
2 const [value, setValue] = useState('');
3
4 return (
5 <div className="space-y-2">
6 <p className="text-sm text-neutral-700 dark:text-neutral-300">
7 Enter the 6-digit code sent to +1 (555) 000-0000
8 </p>
9 <InputOTP
10 length={6}
11 value={value}
12 onValueChange={setValue}
13 separator={<span className="text-neutral-400">-</span>}
14 separatorIndexes={[3]}
15 />

Disabled State

tsx
1function DisabledInputOTP() {
2 return (
3 <InputOTP
4 length={6}
5 disabled
6 defaultValue="123456"
7 />
8 );
9}

Features

  • Accessible - Built with ARIA support and keyboard navigation
  • Copy & Paste - Supports copying and pasting OTP codes
  • Multiple Variants - Default, outline, and filled styles
  • Size Options - Small, medium, and large sizes
  • Customizable - Flexible styling with Tailwind CSS
  • Auto-focus - Automatically moves focus to the next input
  • Masked Input - Option to hide entered digits
  • Separators - Add custom separators between input groups
  • Dark Mode - Built-in dark mode support
  • TypeScript - Full TypeScript support with proper types
  • No Dependencies - Built with only React and Tailwind CSS

Implementation

InputOTP.tsx
tsx
1'use client';
2
3import React, {
4 useState,
5 useRef,
6 useEffect,
7 KeyboardEvent,
8 ChangeEvent,
9} from 'react';
10import { cn } from '@/lib/cn';
11
12interface InputOTPProps {
13 length?: number;
14 value?: string;
15 defaultValue?: string;