How to create OTP Input Box Using React ?
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:
- React
- HTML, CSS and JavaScript
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:
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/.