Building an OTP Input Component in React

A step-by-step guide to creating a 5-digit OTP input with automatic focus management

✅ 1. Initial Setup and State Management

Start by importing essential React hooks and defining component state:

import { useEffect, useRef, useState } from "react";

const DIGIT_COUNT = 5;
const [value, setValue] = useState(new Array(DIGIT_COUNT).fill(""));
const refArr = useRef([]);

Then, inside the component App, you define:

  • DIGIT_COUNT – how many OTP input boxes you want
  • value – state array to store each digit
  • refArr – a mutable array to store refs for input boxes

✅ 2. Initial Focus Management

Use useEffect to focus the first input on component mount:

useEffect(() => {
  refArr.current[0]?.focus();
}, []);

✅ 3. Input Change Handling

Implementation logic for handling input changes:

const handleOnChange = (val, index) => {
  val = val.trim();
  const newArray = [...value];
  newArray[index] = val.slice(-1); // Only last character
  setValue(newArray);

  val && refArr.current[index + 1]?.focus();
};

This ensures only the last digit is used when pasting multiple characters

You define handleOnChange to:

  • Accept only the last character typed
  • Update the state for the current index
  • Automatically focus the next input box if there is input

✅ 4. Backspace Handling

Implement backward navigation for better UX:

const handleOnKeyDown = (e, index) => {
  if (!value[index] && e.key === "Backspace") {
    refArr.current[index - 1]?.focus();
  }
};

This improves UX for deleting digits quickly.

You define handleOnKeyDown to handle backward navigation:

  • If the current input is empty and the user presses Backspace, move focus to the previous input

✅ 5. Rendering the OTP Boxes

{value.map((val, index) => {
                return (
                  <input
                    className="box"
                    key={index}
                    type="number"
                    value={value[index]}
                    onChange={(e) => handleOnChange(e.target.value, index)}
                    ref={(val) => (refArr.current[index] = val)}
                    onKeyDown={(e) => handleOnKeyDown(e, index)}
                  />
                );
              })}

You map over the value array and render an <input> for each digit:

  • Assign ref so you can control focus
  • Limit input to one number
  • Call the respective handlers for onChange and onKeyDown

✅ 6. Styling (Assumed from styles.css)

Each input is styled with the .box class to look uniform and user-friendly. A typical CSS for OTP boxes might look like:

.box {
              width: 40px;
              height: 40px;
              font-size: 24px;
              text-align: center;
              margin: 0 5px;
            }

✅ 7. Resulting Behavior

The final OTP input component:

  • Renders 5 separate input boxes
  • Focuses the first input on load
  • Automatically moves focus as you type or backspace
  • Accepts only 1 digit per box
  • Provides a smooth user experience like most OTP inputs on modern apps

Key Features

  • Automatic focus progression
  • Single character validation
  • Backspace navigation
  • Paste handling