Open In App

How to create OTP Input Box Using React ?

Last Updated : 14 Feb, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Let us explore the implementation of OTP (One-Time Password) login functionality using ReactJS. OTP-based logins add an extra layer of security to your applications, making them more resilient against unauthorized access.

Prerequisites:

Approach:

  • Create a React functional component managing the OTP input state using useState.
  • Utilize useEffect to focus on the first input during component mount.
  • Map through OTP values, rendering individual input boxes with associated refs.
  • Implement onChange to update OTP values and move focus to the next box if a valid number is entered.
  • Use onKeyDown to delete current input and move focus to the previous box on backspace/delete.
  • Apply CSS styles for dimensions and appearance.
  • Integrate the OTP component into a parent component, managing overall input flow and potential API calls.

Steps to Create the Project:

Step 1: Set Up Your React App with Vite:

npm create vite@latest

Step 2: Navigate to the Project Directory

cd otp-login

Step 3: Install the package dependency.

npm install

Project Structure:

Screenshot-2024-02-09-204756

project structure

The updated dependencies in package.json file will look like:

"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"web-vitals": "^2.1.4"
}

Example: Below are the example to show OTP input box using ReactJS.

  • OtpInput.jsx: This file contains the OTP input component logic, handling user input, triggering OTP submission, and managing focus.
  • PhoneOtpForm.jsx: The main form component responsible for handling phone number input, form submission, and rendering the OTP input conditionally.
  • App.css Stylesheet for styling the components and providing a visually appealing design.

Javascript




// OtpInput.jsx
import { useEffect, useRef, useState }
    from "react";
 
const OtpInput = ({ length = 4,
    onOtpSubmit = () => { } }) => {
    const [otp, setOtp] = useState(
        new Array(length).fill(""));
    const inputRefs = useRef([]);
 
    useEffect(() => {
        if (inputRefs.current[0]) {
            inputRefs.current[0].focus();
        }
    }, []);
 
    const handleChange = (index, e) => {
        const value = e.target.value;
        if (isNaN(value)) return;
 
        const newOtp = [...otp];
        // allow only one input
        newOtp[index] =
            value.substring(value.length - 1);
        setOtp(newOtp);
 
        // submit trigger
        const combinedOtp = newOtp.join("");
        if (combinedOtp.length === length)
            onOtpSubmit(combinedOtp);
 
        // Move to next input if current field is filled
        if (value && index < length - 1 &&
            inputRefs.current[index + 1]) {
            inputRefs.current[index + 1].focus();
        }
    };
 
    const handleClick = (index) => {
        inputRefs.current[index].setSelectionRange(1, 1);
 
        // optional
        if (index > 0 && !otp[index - 1]) {
            inputRefs.current[otp.indexOf("")].focus();
        }
    };
 
    const handleKeyDown = (index, e) => {
        if (
            e.key === "Backspace" &&
            !otp[index] &&
            index > 0 &&
            inputRefs.current[index - 1]
        ) {
            // Move focus to the previous input field on backspace
            inputRefs.current[index - 1].focus();
        }
    };
 
    return (
        <div>
            {otp.map((value, index) => {
                return (
                    <input
                        key={index}
                        type="text"
                        ref={(input) => (inputRefs.current[index] = input)}
                        value={value}
                        onChange={(e) => handleChange(index, e)}
                        onClick={() => handleClick(index)}
                        onKeyDown={(e) => handleKeyDown(index, e)}
                        className="otpInput"
                    />
                );
            })}
        </div>
    );
};
export default OtpInput;


Javascript




// PhoneOtpForm.jsx
import { useState } from "react";
import OtpInput from "./OtpInput";
 
const PhoneOtpForm = () => {
    const [phoneNumber, setPhoneNumber] = useState("");
    const [showOtpInput, setShowOtpInput] = useState(false);
 
    const handlePhoneNumber = (event) => {
        setPhoneNumber(event.target.value);
    };
 
    const handlePhoneSubmit = (event) => {
        event.preventDefault();
 
        // phone validations
        const regex = /[^0-9]/g;
        if (phoneNumber.length < 10
            || regex.test(phoneNumber)) {
            alert("Invalid Phone Number");
            return;
        }
 
        // Call BE API
        // show OTP Field
        setShowOtpInput(true);
    };
 
    const onOtpSubmit = (otp) => {
        console.log("Login Successful", otp);
    };
 
    return (
        <div>
            {!showOtpInput ? (
                <form onSubmit={handlePhoneSubmit}>
                    <input
                        type="text"
                        value={phoneNumber}
                        onChange={handlePhoneNumber}
                        placeholder="Enter Phone Number"
                    />
                    <button type="submit">Submit</button>
                </form>
            ) : (
                <div>
                    <p>Enter OTP sent to {phoneNumber}</p>
                    <OtpInput length={4}
                        onOtpSubmit={onOtpSubmit} />
                </div>
            )}
        </div>
    );
};
 
export default PhoneOtpForm;


Javascript




// App.jsx
import "./App.css";
import PhoneOtpForm from "./components/PhoneOtpForm";
 
function App() {
    return (
        <div className="App">
            <h1>Login with Phone</h1>
            <PhoneOtpForm />
        </div>
    );
}
 
export default App;


Javascript




// main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
 
ReactDOM.createRoot(document.getElementById("root")).render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
);


CSS




/* App.css  */
.App {
  font-family: sans-serif;
  text-align: center;
}
 
.otpInput {
  width: 40px;
  height: 40px;
  margin: 5px;
  text-align: center;
  font-size: 1.2em;
}


Steps to run the application:

npm run dev



Output: Now go to http://localhost:5173/.

Recording-2024-02-09-205621-(1)

Output



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads