@leo-showdar/react-native-otp-input-kit
v0.1.0
Published
Lightweight React Native TypeScript components for OTP input and resend timer flows.
Maintainers
Readme
@leo-showdar/react-native-otp-input-kit
Lightweight React Native TypeScript components for OTP input and resend timer flows.
The package is designed for login, phone verification, email verification, and two-step verification screens. It ships only React Native components and hooks, with no native modules and no runtime dependencies beyond React and React Native.
Installation
npm install @leo-showdar/react-native-otp-input-kitBasic Usage
import * as React from 'react';
import { Alert, SafeAreaView, StyleSheet, Text } from 'react-native';
import { OtpInput, OtpResendTimer } from '@leo-showdar/react-native-otp-input-kit';
export function VerifyPhoneScreen() {
return (
<SafeAreaView style={styles.screen}>
<Text style={styles.title}>Enter the code sent to your phone</Text>
<OtpInput
autoFocus
length={6}
onComplete={(code) => Alert.alert('Code complete', code)}
/>
<OtpResendTimer onResend={() => Alert.alert('New code sent')} />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
padding: 24,
rowGap: 20,
},
title: {
color: '#111827',
fontSize: 20,
fontWeight: '700',
textAlign: 'center',
},
});Controlled Usage
import * as React from 'react';
import { Button, View } from 'react-native';
import { OtpInput } from '@leo-showdar/react-native-otp-input-kit';
export function ControlledOtpExample() {
const [code, setCode] = React.useState('');
return (
<View>
<OtpInput
value={code}
onChangeCode={setCode}
allowCellSelection={false}
onComplete={(completedCode) => {
// Submit completedCode to your verification endpoint.
}}
/>
<Button title="Clear" onPress={() => setCode('')} />
</View>
);
}Custom Styles
import { OtpInput } from '@leo-showdar/react-native-otp-input-kit';
export function StyledOtpExample() {
return (
<OtpInput
containerStyle={{ justifyContent: 'center' }}
cellStyle={{
backgroundColor: '#F9FAFB',
borderColor: '#CBD5E1',
borderRadius: 12,
height: 56,
width: 52,
}}
focusedCellStyle={{
borderColor: '#0F766E',
borderWidth: 2,
}}
filledCellStyle={{
borderColor: '#64748B',
}}
textStyle={{
color: '#0F172A',
fontSize: 24,
}}
/>
);
}Error State
import * as React from 'react';
import { Text, View } from 'react-native';
import { OtpInput } from '@leo-showdar/react-native-otp-input-kit';
export function OtpErrorExample() {
const [code, setCode] = React.useState('');
const [hasError, setHasError] = React.useState(false);
return (
<View>
<OtpInput
value={code}
onChangeCode={(nextCode) => {
setCode(nextCode);
setHasError(false);
}}
onComplete={() => setHasError(true)}
error={hasError}
/>
{hasError ? (
<Text style={{ color: '#B91C1C', marginTop: 8 }}>
That code did not match. Check the message and try again.
</Text>
) : null}
</View>
);
}Secure OTP
import { OtpInput } from '@leo-showdar/react-native-otp-input-kit';
export function SecureOtpExample() {
return <OtpInput secureTextEntry length={6} />;
}Resend Timer
import * as React from 'react';
import { Text, View } from 'react-native';
import { OtpResendTimer } from '@leo-showdar/react-native-otp-input-kit';
export function ResendTimerExample() {
const [message, setMessage] = React.useState('');
return (
<View>
<OtpResendTimer
seconds={45}
countdownLabel="Request a new code in"
resendLabel="Send a new code"
onResend={() => setMessage('A new verification code was sent.')}
/>
{message ? <Text>{message}</Text> : null}
</View>
);
}OtpInput Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| length | number | 6 | Number of OTP digits. |
| value | string | undefined | Controlled OTP value. |
| defaultValue | string | '' | Initial value for uncontrolled usage. |
| onChangeCode | (code: string) => void | undefined | Called when the OTP changes. |
| onComplete | (code: string) => void | undefined | Called when the OTP reaches length. |
| allowCellSelection | boolean | true | Allows tapping any cell to edit it. Set false for sequential input and right-to-left delete behavior. |
| autoFocus | boolean | false | Focuses the first cell on mount. |
| disabled | boolean | false | Disables editing and lowers opacity. |
| error | boolean | false | Applies error styling and accessibility state. |
| secureTextEntry | boolean | false | Masks entered digits. |
| placeholder | string | undefined | Placeholder shown in empty cells. |
| keyboardType | TextInputProps['keyboardType'] | 'number-pad' | Keyboard type for each cell. |
| containerStyle | ViewStyle | undefined | Style override for the cell row. |
| cellStyle | ViewStyle | undefined | Base style override for each cell. |
| focusedCellStyle | ViewStyle | undefined | Style applied to the focused cell. |
| filledCellStyle | ViewStyle | undefined | Style applied to cells with a value. |
| errorCellStyle | ViewStyle | undefined | Style applied when error is true. |
| textStyle | TextStyle | undefined | Text style override for each cell. |
| testID | string | undefined | Test id for the container and cell ids. |
OtpResendTimer Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| seconds | number | 60 | Countdown duration in seconds. |
| autoStart | boolean | true | Starts the countdown on mount. |
| onResend | () => void | undefined | Called when resend is pressed. |
| onFinish | () => void | undefined | Called when the countdown reaches zero. |
| renderText | (remainingSeconds: number, canResend: boolean) => React.ReactNode | undefined | Custom text renderer. |
| resendLabel | string | 'Resend code' | Label shown when resend is available. |
| countdownLabel | string | 'Resend code in' | Label shown while counting down. |
| disabled | boolean | false | Prevents resend presses. |
| textStyle | TextStyle | undefined | Style for default countdown text. |
| resendTextStyle | TextStyle | undefined | Style for default resend text. |
| testID | string | undefined | Test id for the pressable timer. |
License
MIT
