New

OTP Input

6-digit OTP/verification code input with auto-focus and paste support.

Forms & Inputotpverificationcodeinput2fa

Dependencies

Other dependencies:

@/components/ui/card@/components/ui/button@/components/ui/input

How to use this component

Copy the code below into your project. Make sure you have the required shadcn/ui dependencies installed. Then import and use the component in your pages or layouts.

Code

1"use client";
2
3import { useState, useRef, useEffect } from 'react';
4import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
5import { Button } from '@/components/ui/button';
6import { Input } from '@/components/ui/input';
7
8export default function OTPInput() {
9 const [values, setValues] = useState(['', '', '', '', '', '']);
10 const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
11
12 useEffect(() => {
13 inputRefs.current[0]?.focus();
14 }, []);
15
16 const handleChange = (index: number, value: string) => {
17 const newValues = [...values];
18 newValues[index] = value.slice(-1);
19 setValues(newValues);
20
21 if (value && index < 5) {
22 inputRefs.current[index + 1]?.focus();
23 }
24 };
25
26 const handleKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {
27 if (e.key === 'Backspace' && !values[index] && index > 0) {
28 inputRefs.current[index - 1]?.focus();
29 }
30 };
31
32 const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
33 e.preventDefault();
34 const pastedData = e.clipboardData.getData('text').slice(0, 6).split('');
35 if (pastedData.every(char => /^[0-9]$/.test(char))) {
36 const newValues = [...values];
37 pastedData.forEach((char, i) => {
38 if (i < 6) newValues[i] = char;
39 });
40 setValues(newValues);
41 const nextEmptyIndex = newValues.findIndex(v => v === '');
42 inputRefs.current[nextEmptyIndex === -1 ? 5 : nextEmptyIndex]?.focus();
43 }
44 };
45
46 const handleVerify = () => {
47 const code = values.join('');
48 if (code.length === 6) {
49 alert(`Verified code: ${code}`);
50 }
51 };
52
53 return (
54 <Card className="w-full max-w-md mx-auto">
55 <CardHeader className="text-center">
56 <CardTitle>Verification Code</CardTitle>
57 <p className="text-sm text-muted-foreground">
58 Enter the 6-digit code sent to your email
59 </p>
60 </CardHeader>
61 <CardContent className="space-y-6">
62 <div className="flex justify-center gap-2">
63 {values.map((value, index) => (
64 <Input
65 key={index}
66 ref={el => inputRefs.current[index] = el}
67 type="text"
68 inputMode="numeric"
69 maxLength={1}
70 value={value}
71 onChange={e => handleChange(index, e.target.value)}
72 onKeyDown={e => handleKeyDown(index, e)}
73 onPaste={handlePaste}
74 className="w-12 h-12 text-center text-2xl font-mono"
75 />
76 ))}
77 </div>
78
79 <Button onClick={handleVerify} className="w-full" size="lg">
80 Verify
81 </Button>
82
83 <div className="text-center space-y-2">
84 <p className="text-sm text-muted-foreground">
85 Didn't receive the code?{' '}
86 <button className="text-primary hover:underline font-medium">
87 Resend
88 </button>
89 </p>
90 <p className="text-xs text-muted-foreground">
91 Resend in 00:30
92 </p>
93 </div>
94 </CardContent>
95 </Card>
96 );
97}

Related Forms & Input Components

Command Palette

Search for a command to run...