In today’s digital world, verifying user identity is crucial for secure applications, and one of the most common ways to do so is through One-Time Passwords (OTP). This blog will walk you through a React component for implementing a phone number login feature with OTP verification. You’ll learn how to handle form validations, manage state effectively, and create a smooth user experience for OTP entry.
What this Blog Will Teach You:
- Phone Number Validation:
- Length Check: The phone number must be exactly 10 digits long. If the phone number is shorter or longer, the system will prompt the user to correct it.
- Input Masking: As the user types, non-numeric characters will be removed automatically, allowing only valid digits to be entered.
- OTP Input Validations:
- Numeric Only: The OTP input only allows numeric characters. Any non-numeric characters will be automatically filtered out, ensuring only valid digits are entered.
- Length Validation: The OTP input is restricted to exactly 4 digits, matching the standard OTP format. The user will be alerted if the OTP length is incorrect.
- Focus Management: As the user enters a digit in one OTP field, the focus will automatically shift to the next field to facilitate smoother input.
- Backspace Handling: If the user needs to correct the OTP, pressing the “Backspace” key will move the focus to the previous field if it is empty, allowing for easy corrections.
- User Interaction Improvements:
- Real-Time Feedback: The system will provide real-time validation as the user types the phone number, alerting them of any errors, such as incorrect number of digits.
Code Walkthrough
1. App.jsx – The Entry Point
import "./App.css"; import PhoneOtpForm from "./components/phone-login"; function App() { return ( <div className="App"> <h1>Login with Phone</h1> {/* Render the Phone OTP Form Component */} <PhoneOtpForm /> </div> ); } export default App;
2. App.css – Basic Styling
.App { font-family: sans-serif; text-align: center; } .otpInput { width: 40px; height: 40px; margin: 5px; text-align: center; font-size: 1.2em; }
3. PhoneLogin.jsx – Phone Input and OTP Handling
import { useState } from "react"; import OtpInput from "./OtpInput"; const PhoneOtpForm = () => { // State to store the entered phone number const [phoneNumber, setPhoneNumber] = useState(""); // State to toggle OTP input visibility const [showOtpInput, setShowOtpInput] = useState(false); // Handles phone number input and ensures only numeric values const handlePhoneNumber = (event) => { setPhoneNumber(event.target.value.replace(/[^0-9]/g, "")); }; // Validates phone number length and simulates sending OTP const handlePhoneSubmit = (event) => { event.preventDefault(); if (phoneNumber.length !== 10) { alert("Please enter a valid 10-digit phone number."); return; } console.log("OTP Sent to: ", phoneNumber); // Mock API call setShowOtpInput(true); }; // Handles OTP submission const onOtpSubmit = (otp) => { console.log("Login Successful with OTP: ", otp); // Log OTP for debugging }; return ( <div> {!showOtpInput ? ( <form onSubmit={handlePhoneSubmit}> <input type="tel" value={phoneNumber} onChange={handlePhoneNumber} placeholder="Enter Phone Number" maxLength={10} /> <button type="submit">Submit</button> </form> ) : ( <div> <p>Enter OTP sent to {phoneNumber}</p> <OtpInput length={4} onOtpSubmit={onOtpSubmit} /> </div> )} </div> ); }; export default PhoneOtpForm;
Explanation:
- State Management:
phoneNumber
: Stores the phone number entered by the user.showOtpInput
: Controls whether the OTP input fields are displayed. Initially set tofalse
, indicating that the OTP fields are not shown until the phone number is validated.
- Handling Phone Number Input:
- The
handlePhoneNumber
function ensures that only numeric values are accepted in the phone number input field. The regular expression/[^0-9]/g
replaces any non-numeric characters with an empty string.
- The
- Phone Number Validation and OTP Sending:
- When the user submits the phone number, the
handlePhoneSubmit
function checks if the phone number is exactly 10 digits long. If not, an alert is shown asking for a valid 10-digit number. - If the number is valid, a mock API call simulates sending the OTP to the phone number, and the OTP input fields are shown by setting
showOtpInput
totrue
.
- When the user submits the phone number, the
- OTP Submission:
- When the OTP is successfully entered, the
onOtpSubmit
function is triggered. In the current code, it simply logs the OTP to the console for debugging.
- When the OTP is successfully entered, the
- Conditional Rendering:
- If the
showOtpInput
state isfalse
(phone number not validated), a phone number input form is displayed. - If
showOtpInput
istrue
, the OTP input fields are shown using theOtpInput
component.
- If the
4. OtpInput.jsx- OTP Input Handling
import { useRef, useState, useEffect } from "react"; const OtpInput = ({ length = 4, onOtpSubmit = () => {} }) => { // State to store each OTP digit const [otp, setOtp] = useState(new Array(length).fill("")); // References for input fields to handle focus const inputRefs = useRef([]); // Focus the first input box when the component loads useEffect(() => { inputRefs.current[0]?.focus(); // Focus first input field on load }, []); // Handles changes in each OTP input field const handleChange = (index, e) => { const value = e.target.value.replace(/[^0-9]/g, ""); // Allow only numbers const newOtp = [...otp]; newOtp[index] = value; // Update value at specified index setOtp(newOtp); // Combine OTP values and submit when complete const combinedOtp = newOtp.join(""); if (combinedOtp.length === length) { console.log("Submitted OTP: ", combinedOtp); // Debugging OTP value onOtpSubmit(combinedOtp); } // Move focus to the next input field if (value && index < length - 1) { inputRefs.current[index + 1]?.focus(); } }; // Handles "Backspace" key press to move focus to the previous input field 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(); } }; // Handles the submit button click const handleSubmit = () => { const combinedOtp = otp.join(""); if (combinedOtp.length === length) { onOtpSubmit(combinedOtp); // Submit OTP if valid } else { alert("Please enter the complete OTP."); } }; return ( <div> {otp.map((value, index) => ( <input key={index} ref={(el) => (inputRefs.current[index] = el)} value={value} onChange={(e) => handleChange(index, e)} onKeyDown={(e) => handleKeyDown(index, e)} // Add the keydown event listener maxLength={1} className="otpInput" /> ))} <button onClick={handleSubmit}>Submit OTP</button> {/* Submit Button */} </div> ); }; export default OtpInput;
Explanation of the handleChange Function:
handleChange
is triggered when the user types in an OTP input field.
Numeric Input Only:- It restricts the input to numbers by removing any non-numeric characters using
e.target.value.replace(/[^0-9]/g, "")
.
- The function updates the corresponding position in the
otp
array with the new value entered by the user.
- If a number is entered and it’s not the last field (
index < length - 1
), the focus automatically shifts to the next input field, making it easier for the user to fill in the OTP without extra clicks.
- When all the input fields are filled (
combinedOtp.length === length
), the complete OTP is combined into a string and passed to theonOtpSubmit
function for further processing (like OTP verification).
- It restricts the input to numbers by removing any non-numeric characters using
Explanation of the handleKeyDown Function:
handleKeyDown
is triggered when a key is pressed inside an OTP input field.
Backspace Key:- If the user presses “Backspace” and the current input field is empty (
!otp[index]
), the focus will move to the previous input field (if it exists) by checkingindex > 0
. - This allows the user to easily move backward when they want to delete characters from the previous fields..
- If the user presses “Backspace” and the current input field is empty (
Explanation of the handleSubmit Function:
handleChange
is triggered when the user types in an OTP input field.
Combining OTP:- It combines all the input field values into a single string (
combinedOtp
).
- It combines all the input field values into a single string (
- If the combined OTP string length matches the required length (
combinedOtp.length === length
), it calls theonOtpSubmit
function to process the OTP. - If the OTP is incomplete, it alerts the user to enter the complete OTP, ensuring that all fields are filled before submission.
Conclusion
This setup shows how to build an OTP-based login system in ReactJS that’s easy to manage and grow. It uses React features like hooks for managing state, and conditional rendering to create a user-friendly interface that adjusts as needed.
The system is designed to handle OTP verification smoothly. It includes features like real-time validation, automatic focus shifting between input fields, and ensuring only numeric input is accepted. This helps make the process of entering OTPs quick and hassle-free.
For real-world use, you can connect this system with services like Twilio or Firebase Authentication. These services send OTPs via SMS, adding a layer of security and convenience for users, making the whole login process smoother and more reliable.