OTP verification is a crucial for secure onboarding and user validation in any mobile application. Let’s take a look at how we can build this using react-native.

On this screen we are going to build the following features.

  1. Focusing OTP Text Input Automatically
  2. Clearing Text Inputs In Reverse Order On Backspace
  3. Using react-native-otp-verify Library For Creating hash(unique for application) and Reading OTP SMS.
  4. Showing Timer for Resend OTP Button.
  5. Submitting OTP Automatically.

We will be using react-native version: 0.63.3

Let’s start with installing react-native-otp-verify. You can get the Npm package here: https://www.npmjs.com/package/react-native-otp-verify

With npm:

$ npm install react-native-otp-verify --save

With yarn

$ yarn react-native-otp-verify

Linking the package is automatic in the latest versions of react native. For manual linking use:

$ react-native link react-native-otp-verify

Manual installation:

  1. Open up
android/app/src/main/java/com/deliveryapp/MainApplication.java

and add

import com.faizal.OtpVerify.RNOtpVerifyPackage;

to the imports at the top of the file.

2. Append the following lines to android/settings.gradle

include ':react-native-otp-verify'
project(':react-native-otp-verify').projectDir = new File(rootProject.projectDir, 	'../node_modules/react-native-otp-verify/android')

Now let’s create the OTP Verification screen component:

We will keep the UI simple with using just four text boxes and one verify button.

otp-verification-page-ui
The OTP screen UI
<View style={styles.container}>
<Text style={styles.headerText}>Verification</Text>
<Image source={ForgotPasswordImage} style={styles.lockImage} />
<View style={{ marginTop: 20, alignItems: 'center' }}>
<Text style={styles.messageText1}>
Enter the verification code we
</Text>
<Text style={styles.messageText1}>
just sent you on your mobile number.
</Text>
</View>
<View style={styles.inputView}>
{[ 0, 1, 2, 3].map(
(element, i) => (
<CustomTextInput
value={otpArray[i]}
                             keyboardType="numeric"
                             textContentType="oneTimeCode"
                             maxLength={1}
                             key={i}
                             autoFocus={i === 0 ? true : undefined}
/>
)
)}
</View>
<CustomButton
     buttonStyle={styles.submitButton}
           text="Verify"
           type="fill"
           onPress={onSubmitButtonPress}
           textStyle={styles.submitText}
/>
</View>

Auto focusing of Text Input boxes

While entering OTP, no one wants to enter a digit, manually click on the next input field, enter the second digit and so on. To programmatically focus the next TextInput box, we are going to assign reference to each TextInput using useRef() hook and handle the behaviour in onTextChange callback.

// refs to focus input programmatically 
const inputRef1 = useRef(null);
const inputRef2 = useRef(null);
const inputRef3 = useRef(null);
const inputRef4 = useRef(null);

const onOptChange = (index) => {
return (value) => {
           if (isNaN(Number(value))) {
               return;
           }
           const otpArrayCpy = otpArray.concat();
           otpArrayCpy[index] = value;
           setOtpArray(otpArrayCpy);
           if (value !== '') {
               if (index === 0) {
                   inputRef2.current.focus();
               } else if (index === 1) {
                   inputRef3.current.focus();
               } else if (index === 2) {
                   inputRef4.current.focus();
               }
           }
};
};

Adding refs to TextInput

<View style={styles.inputView}>
{[inputRef1, inputRef2, inputRef3, inputRef4].map(
      	(inputRef, i) => (
           		<CustomTextInput
                 		refCallback={refCallback(inputRef)}
                       onChangeText={onOptChange(i)}
                       onKeyPress={onOTPKeyPress(i)}
                       value={otpArray[i]}
                       keyboardType="numeric"
                       textContentType="oneTimeCode"
                       maxLength={1}
                       key={i}
                       autoFocus={i === 0 ? true : undefined}
                       inputStyle={styles.inputStyle}
                       containerStyle={{ flex: 0.15 }}
            	/>
      	)
)}
</View>

Time for Resend OTP Link

Users may not receive OTP instantly. There can be numerous reasons for this e.g network provider problem. So it is important to provide users a Resend button.

To implement this, we use the state variable:

resendButtonDisableTime

// in seconds, if the value is greater than 0, the button will be disabled
const [resendButtonDisableTime, setResendButtonDisableTime] = useState(
       RESEND_OTP_TIME_LIMIT
);
<View style={styles.resendTextView}>
{resendButtonDisableTime > 0 
? (<Text>Resend OTP in {resendButtonDisableTime}</Text>) 
: (
      	<Pressable onPress={onResendButtonClick}>
            		<Text
                  		style={[
                        		styles.messageText2,
                              styles.resendStyle
                        ]}>
                               Resend OTP!
                 	</Text>
            </Pressable>
      )}
</View>

Next, we define a function startResendOtpTimer which keeps decrementig the value of resendButtonDisableTime.

const startResendOtpTimer = () => {
       if (resendOtpTimerInterval) {
           clearInterval(resendOtpTimerInterval);
       }
       resendOtpTimerInterval = setInterval(() => {
           if (resendButtonDisableTime <= 0) {
               clearInterval(resendOtpTimerInterval);
           } else {
               setResendButtonDisableTime(resendButtonDisableTime - 1);
           }
       }, 1000);
   };

Call this function in useEffect. (componentDidUpdate)

useEffect(() => {
       startResendOtpTimer();
       return () => {
           if (resendOtpTimerInterval) {
               clearTimeout(resendOtpTimerInterval);
           }
       };
   }, [resendButtonDisableTime]);

Clearing TextInputs in reverse order, on pressing Backspace

Since we are automatically focusing next TextInput, It also makes sense to clear previous OTP digits on pressing backspace. The onChangeText event of TextInput will be fired only when there is actual change in text, which means it won’t be triggered on pressing backspace when the text is already blank. To achieve this functionality we will add a listener on onKeyPress event.

const onOTPKeyPress = (index) => {
       return ({ nativeEvent: { key: value } }) => {
//autofocus to previous input if the value is blank and existing value is also blank
           if (value === 'Backspace' && otpArray[index] === '') {
               if (index === 1) {
                   inputRef1.current.focus();
               } else if (index === 2) {
                   inputRef2.current.focus();
               } else if (index === 3) {
                   inputRef3.current.focus();
               }
               if (isAndroid && index > 0) {
                   const otpArrayCpy = otpArray.concat();
                   otpArrayCpy[index - 1] = '';
                   setOtpArray(otpArrayCpy);
               }
           }
       };
   };

Note: onKeyPress on Android will only work for soft keyboard.

Auto read OTP SMS

This feature is not crucial, but it helps to improve the UX of the application. To automatically read the SMS, the SMS Retriever API is used. With the SMS Retriever API, you can perform SMS-based user verification in your Android app automatically, without requiring the user to manually type verification codes, and without requiring any extra app permissions. OTP Message must contain an app’s hash for this feature to work.

The message structure should be like below

<#> Hello from ’your app name’. Your One Time Password for password reset is 9876 and is valid until 12:00 uc+CgduAsnP

To read the sms,

useEffect(() => {
       RNOtpVerify.getOtp()
           .then((p) => {
               RNOtpVerify.addListener((message) => {
                   try {
                       if (message && message !== 'Timeout Error') {
                           const otp = /(\d{4})/g.exec(message)[1];
                           if (otp.length === 4) {
                               setOtpArray(otp.split(''));
                           }
                       } else {
                           console.log( 'OTPVerification: RNOtpVerify.getOtp - message=>', message );
                       }
                   } catch (error) {
                       console.log('OTPVerification: RNOtpVerify.getOtp error=>', error );
                   }
               });
           })
           .catch((error) => {
               console.log(error);
           });

       return () => {
           removeOtpListener();
       };
   }, []);

The hash in the OTP message is unique and it depends on the package name of your application and the keystore(in production the hash may change). To generate the hash for your app, use following function in your App.js

const getHash = () => {
       RNOtpVerify.getHash()
           .then((hash) => {
               console.log('App.js: Application hash is=> ', hash);
		//message example
               console.log(`<#> Dear User,
               1091 is your OTP for verification. (Remaining Time: 10 minutes and 0 seconds)
                ${hash[0]}`);
           })
           .catch((error) => {
               console.log(error);
           });
   };

   getHash();

The RNOtpVerify.getHash() function generates the hash specific to the application. Use the generated hash in your OTP message.

Auto submitting the OTP

otp-screen-2
Auto submitting the OTP

The last feature is to automatically submit the OTP in 4 sec once the OTP was read successfully from SMS. The state variable autoSubmitOtpTime is used for this.

Also use the function to show a timer once the OTP was detected successfully.

const startAutoSubmitOtpTimer = () => {
       if (autoSubmitOtpTimerInterval) {
           clearInterval(autoSubmitOtpTimerInterval);
       }
       autoSubmitOtpTimerInterval = setInterval(() => {
           autoSubmitOtpTimerIntervaCallbackRef.current();
      }, 1000);
   };

One catch here is that using a reference autoSubmitOtpTimerIntervaCallbackRef callback created from useRef hook inside setInterval function because its required to updated value of autoSubmitOtpTime state variable. Also I had to update reference variable in useEffect (componentDidUpdate) –

useEffect(() => {
       autoSubmitOtpTimerIntervaCallbackRef.current = autoSubmitOtpTimerIntervaCallback;
   });

const autoSubmitOtpTimerIntervaCallback = () => {
       if (autoSubmitOtpTime <= 0) {
           clearInterval(autoSubmitOtpTimerInterval);

           // submit OTP
           onSubmitButtonPress();
       }
       setAutoSubmitOtpTime(autoSubmitOtpTime - 1);
   };

Let begin building your app!

Now that we have built secure login for onboarding new customers using OTP, you can start building your mobile app!

We at V2STech Solutions have over 7 years of experience building mobile apps for e-governance, edutech, healthcare, field force management, power and utilities, fintech and much more.

Reach out to us to add super powers to your mobile apps with machine learning, predictive analytics, AI and make it scalable with cross platform support.

Leave a Reply