react-native-otp-entry and react-otp-input are UI components designed to handle One-Time Password (OTP) or verification code entry in React applications. react-otp-input is primarily built for React web applications, offering a customizable input field that splits characters into separate boxes. react-native-otp-entry is tailored for React Native mobile environments, focusing on native keyboard handling, auto-fill support, and mobile-specific UX patterns. Both aim to simplify the implementation of secure code entry but target different rendering platforms.
Both react-native-otp-entry and react-otp-input solve the same user problem: entering a short verification code securely. However, they operate in fundamentally different environments. One targets the mobile native ecosystem, while the other targets the web DOM. Choosing the wrong one for your platform will lead to broken builds or poor user experience. Let's compare how they handle input, styling, and platform features.
react-native-otp-entry is built exclusively for React Native.
TextInput components under the hood.keyboardType and autoFill hints.// react-native-otp-entry: Native implementation
import { OTPEntry } from 'react-native-otp-entry';
export default function App() {
return (
<OTPEntry
totalDigits={6}
onTextChange={(text) => console.log(text)}
/>
);
}
react-otp-input is built for React web applications.
<input> elements.// react-otp-input: Web implementation
import OtpInput from 'react-otp-input';
export default function App() {
return (
<OtpInput
numInputs={6}
onChange={(text) => console.log(text)}
renderInput={(props) => <input {...props} />}
/>
);
}
react-native-otp-entry configures the mobile keyboard directly.
keyboardType='numeric' to show the number pad.// react-native-otp-entry: Keyboard config
<OTPEntry
totalDigits={6}
keyboardType="number-pad"
secureTextEntry={false}
onTextChange={(code) => setCode(code)}
/>
react-otp-input relies on HTML input attributes.
type="tel" or inputMode="numeric" for mobile browsers.// react-otp-input: Input mode config
<OtpInput
numInputs={6}
inputType="tel"
inputMode="numeric"
onChange={(code) => setCode(code)}
renderInput={(props) => <input {...props} />}
/>
react-native-otp-entry uses React Native style objects.
style prop accepting a StyleSheet.// react-native-otp-entry: Styling
<OTPEntry
totalDigits={6}
styles={{
digitBox: { borderWidth: 1, borderColor: '#ccc' },
filledBox: { borderColor: '#000' }
}}
onTextChange={(text) => setText(text)}
/>
react-otp-input uses CSS classes or inline styles.
containerStyle or inputStyle props.renderInput.// react-otp-input: Styling
<OtpInput
numInputs={6}
inputStyle={{ border: '1px solid #ccc' }}
containerStyle={{ display: 'flex', gap: '10px' }}
onChange={(text) => setText(text)}
renderInput={(props) => <input {...props} />}
/>
react-native-otp-entry integrates with OS auto-fill.
// react-native-otp-entry: Auto-fill hints
<OTPEntry
totalDigits={6}
textContentType="oneTimeCode"
onTextChange={(text) => setText(text)}
/>
react-otp-input handles clipboard via browser events.
autoComplete="one-time-code" for browser support.// react-otp-input: Auto-complete
<OtpInput
numInputs={6}
autoComplete="one-time-code"
onChange={(text) => setText(text)}
renderInput={(props) => <input {...props} />}
/>
These packages are specialized for OTP flows. Consider alternatives when:
formik or react-hook-form with standard inputs.react-native-otp-text-input.| Feature | react-native-otp-entry | react-otp-input |
|---|---|---|
| Platform | 📱 React Native (iOS/Android) | 🌐 React Web (DOM) |
| Underlying Element | Native TextInput | HTML <input> |
| Styling | React Native StyleSheet | CSS / Inline Styles |
| Auto-Fill | Native OS SMS/Code Fill | Browser autoComplete |
| Keyboard | keyboardType prop | inputMode / type props |
| Paste Support | Native Clipboard | JS Paste Event |
Think in terms of target environment first:
react-native-otp-entry. It leverages native capabilities for SMS auto-fill and keyboard management that web components cannot access.react-otp-input. It is optimized for browser behavior, clipboard pasting, and DOM focus management.Critical Note: Do not attempt to use react-otp-input in a React Native project. It relies on DOM APIs that do not exist in the native runtime. Conversely, react-native-otp-entry will not render in a web browser. Always match the package to your rendering engine.
Choose react-otp-input if you are developing a web application using React.js. It provides robust support for desktop and mobile browsers, handling clipboard pasting and character separation effectively in a DOM environment. This package is the standard choice for web-based login flows where React Native specific native modules are not available.
Choose react-native-otp-entry if you are building a mobile application using React Native. It is optimized for touch interfaces, supports native auto-fill APIs on iOS and Android, and handles soft keyboard interactions correctly. This package is essential when you need the OTP field to integrate seamlessly with mobile OS features like SMS retreival.
A fully customizable, one-time password input component for the web built with React.

npm install --save react-otp-input
No problem! You can find the documentation for v2 here
import React, { useState } from 'react';
import OtpInput from 'react-otp-input';
export default function App() {
const [otp, setOtp] = useState('');
return (
<OtpInput
value={otp}
onChange={setOtp}
numInputs={4}
renderSeparator={<span>-</span>}
renderInput={(props) => <input {...props} />}
/>
);
}
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| numInputs | number | true | 4 | Number of OTP inputs to be rendered. |
| renderInput | function | true | none | A function that returns the input that is supposed to be rendered for each of the input fields.
The function will get two arguments: inputProps and index. inputProps is an object that contains all the props that should be passed to the input being rendered (Overriding these props is not recommended because it might lead to some unexpected behaviour). index is the index of the input being rendered.
|
| onChange | function | true | console.log | Returns OTP code typed in inputs. |
| onPaste | function | false | none | Provide a custom onPaste event handler scoped to the OTP inputs container. Executes when content is pasted into any OTP field.
Example: const handlePaste: React.ClipboardEventHandler |
| value | string / number | true | '' | The value of the OTP passed into the component. |
| placeholder | string | false | none | Specify an expected value of each input. The length of this string should be equal to numInputs. |
| renderSeparator | component / function | false | none | Provide a custom separator between inputs by passing a component. For instance, <span>-</span> would add - between each input. |
| containerStyle | style (object) / className (string) | false | none | Style applied or class passed to container of inputs. |
| inputStyle | style (object) / className (string) | false | none | Style applied or class passed to each input. |
| inputType | <input> type | false | text | The type of the input that will be passed to the input element being rendered. In v2 isInputNum used to set the input type as tel and prevented non numerical entries, so as to avoid the spin buttons added to the inputs with input type number. That behaviour is still supported if you pass tel to the inputType prop. |
| shouldAutoFocus | boolean | false | false | Auto focuses input on initial page load. |
| skipDefaultStyles | boolean | false | false | The component comes with default styles for legacy reasons. Pass true to skip those styles. This prop will be removed in the next major release. |
Do not override the following props on the input component that you return from the renderInput prop. Doing so might lead to unexpected behaviour.
refvalueonChangeonFocusonBluronKeyDownonPasteonInputtypeinputModeThe v3 of react-otp-input is a complete rewrite of the library. Apart from making the API more customizable and flexible, this version is a complete rewrite of the library using TypeScript and React Hooks. Here are the breaking changes that you need to be aware of:
You now need to pass your own custom input component that will be rendered for each of the input fields via renderInput prop. This gives you the flexibility to customize the input fields as you desire. This also means that props like focusStyle, isDisabled, disabledStyle, hasErrored, errorStyle, isInputNum, isInputSecure, data-cy and data-testid are no longer supported. You can achieve the same functionality and more by passing the relevant props directly to the input component that you return from the renderInput prop.
The separator prop has now been renamed to renderSeparator. This prop now apart from accepting a component that will be rendered as a separator between inputs like it used to, now also accepts a function that returns a component. The function will get the index of the separator being rendered as an argument.
A new prop called inputType has been added to the component. This prop can be used to specify the type of the input that will be passed to the input element being rendered. The default value of this prop is number.
react-otp-input is now a controlled component to facilitate functionalities that weren't possible before from the application using it, such as clearing or pre-assigning values. For v1.0.0 and above, a value prop needs to be passed in the component for it to function as expected.
cd example
npm run dev
Feel free to open issues and pull requests!
Thanks goes to these wonderful people (emoji key):
Abhishek Warokar 💻 🎨 🚧 🤔 👀 | Aj 💻 🎨 🤔 | Aromal Anil 💻 🔧 | Gabriele Corti 💻 ️️️️♿️ | Anoop 💻 | Dewansh Rawat 🐛 | Ishan Chhabra 💻 |
yaojie 💻 | Prateek Surana 💻 🚧 🤔 📖 🎨 | Süleyman Barış Eser 🐛 | Steve Domino 💻 |
This project follows the all-contributors specification. Contributions of any kind welcome!