← Components/Inputs & Forms

OTPInput

otpinput-1779378412632.tsx
'use client'
import { useRef, useState, KeyboardEvent, ClipboardEvent } from 'react'

const LENGTH = 6

export default function OTPInput() {
  const [values, setValues] = useState<string[]>(Array(LENGTH).fill(''))
  const [verified, setVerified] = useState<boolean | null>(null)
  const [loading, setLoading] = useState(false)
  const inputs = useRef<(HTMLInputElement | null)[]>([])

  function handleChange(i: number, val: string) {
    if (!/^[0-9]?$/.test(val)) return
    const next = [...values]
    next[i] = val
    setValues(next)
    if (val && i < LENGTH - 1) inputs.current[i + 1]?.focus()
    setVerified(null)
  }

  function handleKey(i: number, e: KeyboardEvent<HTMLInputElement>) {
    if (e.key === 'Backspace' && !values[i] && i > 0) {
      inputs.current[i - 1]?.focus()
    }
  }

  function handlePaste(e: ClipboardEvent) {
    e.preventDefault()
    const text = e.clipboardData.getData('text').replace(/D/g, '').slice(0, LENGTH)
    const next = [...values]
    for (let i = 0; i < text.length; i++) next[i] = text[i]
    setValues(next)
    inputs.current[Math.min(text.length, LENGTH - 1)]?.focus()
  }

  async function verify() {
    const code = values.join('')
    if (code.length < LENGTH) return
    setLoading(true)
    await new Promise(r => setTimeout(r, 1200))
    setVerified(code === '123456')
    setLoading(false)
  }

  const complete = values.every(v => v !== '')

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 24, padding: 48, background: '#0A0A0A' }}>
      <div style={{ textAlign: 'center' }}>
        <div style={{ color: '#F5F5F0', fontWeight: 700, fontSize: 20, marginBottom: 6 }}>Enter verification code</div>
        <div style={{ color: '#666', fontSize: 14 }}>Sent to your email · Use <strong style={{ color: '#C9A84C' }}>123456</strong> to test</div>
      </div>

      <div style={{ display: 'flex', gap: 10 }} onPaste={handlePaste}>
        {values.map((v, i) => (
          <input key={i}
            ref={el => { inputs.current[i] = el }}
            value={v} maxLength={1} inputMode="numeric"
            onChange={e => handleChange(i, e.target.value)}
            onKeyDown={e => handleKey(i, e)}
            autoFocus={i === 0}
            style={{
              width: 48, height: 56, textAlign: 'center', fontSize: 22, fontWeight: 700,
              background: '#111', border: `2px solid ${
                verified === null ? (v ? 'rgba(201,168,76,0.5)' : 'rgba(255,255,255,0.1)') :
                verified ? 'rgba(34,197,94,0.5)' : 'rgba(239,68,68,0.5)'
              }`,
              borderRadius: 10, color: '#F5F5F0', outline: 'none',
              transition: 'border-color 0.2s',
            }}
          />
        ))}
      </div>

      <button onClick={verify} disabled={!complete || loading}
        style={{
          background: complete ? '#C9A84C' : 'rgba(255,255,255,0.06)',
          color: complete ? '#0A0A0A' : '#555',
          border: 'none', padding: '13px 36px', borderRadius: 10,
          cursor: complete ? 'pointer' : 'not-allowed',
          fontWeight: 700, fontSize: 15, transition: 'all 0.2s',
        }}
      >{loading ? '⏳ Verifying...' : verified === true ? '✓ Verified!' : verified === false ? '✗ Invalid code' : 'Verify code'}</button>
    </div>
  )
}

Component info

CategoryInputs & Forms
Frameworkreact
TierFREE
Views0
Copies0

About

One-time password input with auto-focus, paste support, and animated verification

More from Inputs & Forms

'use client';

import { useState, useEffect } from 'react';

interface Tag {
  id: number;
  name: string;
}

const TagInput = () => {
  const [tags, setTags] = useState<Tag[]>([]);
  const [inputValue, setInputValue] = useState('');
  const [suggest
TagInput
Inputs & Forms
'use client';

import React, { useState } from 'react';

interface File {
  name: string;
  size: number;
  progress: number;
}

const FileUpload = () => {
  const [dragOver, setDragOver] = useState(false);
  const [files, setFiles] = useState<File[]
FileUpload
Inputs & Forms
import React, { useState } from 'react';

const OTPInput = () => {
  const [otp, setOtp] = useState(new Array(6).fill(''));
  const [activeIndex, setActiveIndex] = useState(0);

  const handleChange = (e, index) => {
    const value = e.target.value;
OTPInput
Inputs & Forms
'use client';

import React, { useState } from 'react';

interface RangeSliderProps {
  min: number;
  max: number;
  defaultValue: [number, number];
}

const RangeSlider: React.FC<RangeSliderProps> = ({ min, max, defaultValue }) => {
  const [minVal
RangeSlider
Inputs & Forms