- React Native OTP Receiver
This package handles receiving and filling OTP from SMS message. While automatically filling the input from incoming SMS is only available for Android, we handle user pressing the autofill button on iOS keyboard.
Important
For everything to work correctly, you must send SMS in correct format and setup associated domains for iOS
Android | iOS |
---|---|
![]() |
![]() |
This libs is currently available only for the new arch.
yarn add @pavelgric/react-native-otp-receiver
We offer various ways of integration, what could be call low-level and high-level.
In low-level, you can use methods exported for Android to capture incoming SMS and handle iOS your own way
In high-level you use one of the prepared components (you are still responsible for rendering the UI) to easily access and render the OTP. This also has iOS integration ready.
This works in two steps. You register a listener for incoming SMS and then call a method that you are expecting a SMS.
A listener for incoming SMS.
import { onSMSReceived } from 'react-native-otp-receiver';
useEffect(() => {
const listener = onSMSReceived((sms: SMSData) => {
// handle the oncoming sms
});
return () => {
listener.remove();
};
}, []);
the listener receives SMSData with:
{
message?: string;
error?: string;
}
The error could be because of timeout, see expectSMSWithOTP
After calling this function, you will get notified about incoming message via the onSMSReceived
listener. SMS must be correctly formatted, otherwise you won't receive it from the onSMSReceived
.
There is a time window to receive the message (around 5 mins) after calling expectSMSWithOTP
, after this time window passes, onSMSReceived
will receive "error": "TIMEOUT"
, after that no message will be captured and you may want to start expectSMSWithOTP
again.
After calling expectSMSWithOTP
, the onSMSReceived
will capture only one message. If it's not the message you expected, call expectSMSWithOTP
again (but for this to happen, user would have to receive SMS containing the app hash at end, it doesn't matter if user receives any other SMS).
import { expectSMSWithOTP } from 'react-native-otp-receiver';
const res = await expectSMSWithOTP()
// the res is boolean indicating whether the listener was started successfully
Calling this function will display prompt for user to select his own phone number.
import { requestPhoneHint } from 'react-native-otp-receiver';
const res = await requestPhoneHint()
// the res is a string containing the phoneNumber. This call may fail (e.g. user dismisses the prompt)
We try to assume as little as possible about what your code looks like and you should be able to configure following components to your needs.
Prop | Required | Type | Description | Default |
---|---|---|---|---|
codeInputShape | Yes | number[] | Shape for the code input. Number values in the array say how many characters are in each input cell. E.g. [1,1,1,1] is 4 cells with single char in each cell. [3, 3] are 3 chars in two cells |
- |
children | No | ReactNode |
The UI to render | - |
onCodeEntered | No | (data: OnCodeEnteredData) => void |
Function called after user fills in the code in it's full length | - |
onSubmitCode | No | (data: OnSubmitCodeData) => void; |
Function called after user fills in the code in it's full length. In this callback you should do your code validation | - |
onCodeChanges | No | (code: string) => void |
Called whenever the code value changes | - |
parseSMS | No | (sms: string) => string | null | undefined |
(Android only) Function called when user receives the SMS and expects you to return the code extracted from it | - |
expectSMSOnMount | No | boolean | Whether to automatically call expectSMSWithOTP . If not set to true, you must call expectSMSWithOTP manually |
- |
value | No | string | Use to set the code input value manually | - |
parseEnteredCodeChar | No | (char: string) => string |
Modify the entered char that user enters via keyboard. E.g. to capitalize it | - |
parsePastedCode | No | (code: string, codeInputShape: number[]) => string[]; |
When user pastes copied (or suggested) value, here you can parse the value into input cells. E.g. to parse ABC-XYZ into ['ABC', 'XYZ'] |
Function that separates string into the input shape |
validateCodeChar | No | CodeCharValidator | Each time the code changes, you can validate it. This code runs for each entered character. E.g. to only accept numbers | - |
onSMSError | No | (error: string) => void |
Called when onSMSReceived returns error |
- |
Example:
import { View, Button } from 'react-native';
import { OTPInputCell, OTPProvider } from 'react-native-otp-receiver';
import { useOTPContext } from '../../src/components/OTPProvider';
import styles from './styles';
const getCodeFromSMS = (sms: string) => {
// find 6 digit code
return sms.match(/\d{6}/)?.[0] || '';
};
export default function OTPProviderExample() {
return (
<OTPProvider
codeInputShape={[1,1,1,1]} // 4 cells, each holds single char
onCodeEntered={(code) => { /* */ }}
onSubmitCode={(data) => { /* */}}
parseSMS={getCodeFromSMS}
>
<CodeInputRenderer />
</OTPProvider>
);
}
function CodeInputRenderer() {
const { codeInputs, isCodeValid, submitCode } = useOTPContext();
return (
<>
<View style={styles.otpContainer}>
{codeInputs.map((index) => {
return <CodeInputComponent key={index} index={index} />;
})}
</View>
<Button title="Submit" disabled={!isCodeValid} onPress={submitCode} />
</>
);
}
function CodeInputComponent({ index }: { index: number }) {
return <OTPInputCell index={index} />;
}
Full example in the example app
Wraps the OTPProvider
, some may prefer this implementation over using the provider.
Provider props and:
Prop | Required | Type | Description |
---|---|---|---|
children | Yes | (props: RenderProps) => ReactNode |
The UI to render |
CodeInputCellComponent | Yes | (otpHandles: CodeInputCellComponentProps) => ReactElement |
The UI component to render as an input cell |
import { View, Button, TouchableOpacity } from 'react-native';
import {
OTPInputHandler,
OTPInputCell,
type CodeInputCellComponentProps,
} from 'react-native-otp-receiver';
import styles from './styles';
const getCodeFromSMS = (sms: string) => {
return sms.match(/\d{6}/)?.[0] || '';
};
export default function OTPInputHandlerExample() {
return (
<OTPInputHandler
codeInputShape={[1,1,1,1]}
CodeInputCellComponent={CodeInputComponent}
onCodeEntered={(code) => { /* */ }}
onSubmitCode={(code) => { /* */}}
parseSMS={getCodeFromSMS}
>
{({ renderOTPInput, submitCode, isValid }) => (
<>
<View style={styles.otpContainer}>{renderOTPInput()}</View>
<Button title="Submit" disabled={!isValid} onPress={submitCode} />
</>
)}
</OTPInputHandler>
);
}
function CodeInputComponent(inputHandles: CodeInputCellComponentProps) {
return <OTPInputCell {...inputHandles} />;
}
Full example in the example app
The cell that handles input of the code value. It's a wrapper around TextInput
TextInput props and:
Prop | Required | Type | Description |
---|---|---|---|
index | Yes | number | Index of the input cell |
Returns handles to control TextInput component to capture key presses, text changes and others. Only useful if you do not use OTPInputCell
component
Returns input cell state (like value
, isFocused
) and useful handles e.g. to focus input.
You can get the app hash by running this command yarn react-native-otp-receiver get-app-hash
. It will ask you for package name, path to your keystore file and the keystore password.
This means you will have two separate app hashes, one for debug and one for production key.
SMS must include app-hash at the end of the message, otherwise you won't receive it from the onSMSReceived
. Other than that, the message content may be anything. It is up to you to retrieve the code value from the message content, based on what you send.
Example:
Your verification code is 123456
GawQ12fcSiS
You must set up associated domains for your app and add it to the SMS content. Than the message content must by as they say in apple docs
You can present any information at the start of the SMS message, in any format. The last line of your message needs your DNS domain name, prefixed by an at sign (@), and the one-time code, prefixed by a hash symbol (#). Separate the two components using a space. For example:
After receiving this SMS, user will will see a suggestion above their keyboard to fill this code. If you use high-level usage, pressing this code will be captured and filled automatically.
Example:
Your Example code is 123456.
@example.com #123456
iOS doesn't have API for neither reading received SMS messages, nor to get own phone number
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Made with create-react-native-library