ShipNative
PricingBlogFAQContact
Build my app
ShipNative
PricingBlogFAQContact
Build my app
GuideOctober 2026 · 8 min read · By Saurabh Singh

React Native OTP Input: SMS Autofill, Paste, and Boxes

The six-box verification code screen looks like six inputs, and that is exactly how most first attempts build it. Then SMS autofill does nothing, pasting a code fills one box, and backspace gets stuck. The fix is to stop building six inputs. This guide shows the single-input pattern that every reliable OTP component uses, the exact props that turn on autofill on iOS and Android, and where a library or the SMS Retriever API is worth adding.

Why six inputs fail

ApproachSMS autofillPasteBackspace
Six TextInputs, maxLength 1BreaksFirst box onlyNeeds manual focus juggling
One hidden TextInput + drawn boxesWorksWorksWorks
Library (otp-entry, confirmation-code-field)WorksWorksWorks

The operating system delivers an autofilled or pasted code as one string into one field. Six fields with maxLength={1} each catch the first character and drop the rest. You can patch this by catching the string in the first box and splitting it out, then manage focus jumps forward on type and backward on backspace, but every patch adds an edge case. One input that owns the whole code has none of them.

The pattern: one hidden input, six drawn boxes

import { useRef, useState } from 'react';
import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native';

const LENGTH = 6;

export function OtpInput({ onComplete }: { onComplete: (code: string) => void }) {
  const [code, setCode] = useState('');
  const inputRef = useRef<TextInput>(null);

  const handleChange = (text: string) => {
    const digits = text.replace(/\D/g, '').slice(0, LENGTH);
    setCode(digits);
    if (digits.length === LENGTH) onComplete(digits);
  };

  return (
    <Pressable onPress={() => inputRef.current?.focus()} accessible={false}>
      <View style={styles.row}>
        {Array.from({ length: LENGTH }, (_, i) => {
          const active = i === Math.min(code.length, LENGTH - 1);
          return (
            <View key={i} style={[styles.box, active && styles.boxActive]}>
              <Text style={styles.digit}>{code[i] ?? ''}</Text>
            </View>
          );
        })}
      </View>
      <TextInput
        ref={inputRef}
        value={code}
        onChangeText={handleChange}
        maxLength={LENGTH}
        keyboardType="number-pad"
        textContentType="oneTimeCode"   // iOS: code from Messages above keyboard
        autoComplete="sms-otp"          // Android: keyboard suggests the code
        autoFocus
        accessibilityLabel="Verification code"
        style={styles.hidden}
      />
    </Pressable>
  );
}

const styles = StyleSheet.create({
  row: { flexDirection: 'row', gap: 8, justifyContent: 'center' },
  box: { width: 46, height: 56, borderRadius: 10, borderWidth: 1.5, borderColor: '#3a3a3a',
    alignItems: 'center', justifyContent: 'center' },
  boxActive: { borderColor: '#fb923c' },
  digit: { fontSize: 24, color: '#fff', fontVariant: ['tabular-nums'] },
  hidden: { position: 'absolute', opacity: 0, width: 1, height: 1 },
});

The real TextInput is invisible and holds the whole code. The boxes are just a picture of its value. Tapping anywhere on the boxes focuses the input. Because there is one field, the OS autofill lands in it, paste lands in it, and backspace removes the last digit, with no focus logic at all. Stripping non-digits in handleChangealso cleans up pastes like “Your code is 482 913”.

Keep the hidden input at a tiny non-zero size rather than display: none. A field that is not laid out cannot take focus, and some Android keyboards will not show suggestions for it.

Autofill: what each platform actually does

  • iOS. textContentType="oneTimeCode"makes the code from a recent SMS (or email in Mail) appear in the QuickType bar above the keyboard. One tap fills it. No permission and no special SMS format are needed, though messages that say “code” near the number are detected more reliably.
  • Android, keyboard suggestion. autoComplete="sms-otp" lets keyboards that support it, such as Gboard, suggest the code from an incoming SMS. Works in Expo Go.
  • Android, fully automatic.The SMS Retriever API reads the code with no tap at all, but the SMS must end with your app's hash string and you need a native module, so a development build. Worth it for apps where sign-in happens often; overkill for most.

Wiring it to verification

With Supabase phone auth, the screen calls signInWithOtp on the previous step and verifyOtp when the code is complete:

<OtpInput
  onComplete={async (token) => {
    setVerifying(true);
    const { error } = await supabase.auth.verifyOtp({ phone, token, type: 'sms' });
    setVerifying(false);
    if (error) setError('That code did not work. Check it or request a new one.');
  }}
/>

Three details users notice: submit automatically on the sixth digit instead of making them find a button, clear the code and refocus on an error so they can retype immediately, and show a resend link with a visible countdown (30 to 60 seconds) so they do not hammer it. Email codes work the same way with type: 'email'.

Build it or install it?

react-native-otp-entry and react-native-confirmation-code-field both use the single-input approach, are pure JavaScript, and add focus animations and caret styling. Pick one if you want that polish without writing it. If the component above already matches your design, it is fewer moving parts than a dependency you will need to update.

Phone sign-in is a whole flow, not one input: country code, rate limits, resend, error states. Describe it to ShipNative, “phone login with a 6-digit SMS code that autofills, resend after 30 seconds, Supabase auth”, and it generates the screens and the auth calls as real React Native that you can test on your own phone with a real SMS.

Frequently Asked Questions

How do I make an OTP input autofill from SMS in React Native?

On iOS, set textContentType="oneTimeCode" on the TextInput; the code appears above the keyboard when the SMS arrives. On Android, set autoComplete="sms-otp" so supported keyboards offer the code. Both only work when there is a single TextInput receiving the whole code, which is why the one-hidden-input pattern matters.

Why does paste only fill the first box of my OTP input?

Because the code is split across six TextInputs, each with maxLength 1, so a pasted six-digit string lands in one box and is truncated. Use one TextInput that holds the full code and draw the six boxes from its value; paste, autofill, and backspace then work for free.

Should I use a library for the OTP input?

Libraries such as react-native-otp-entry and react-native-confirmation-code-field use the same single-input approach and work in Expo Go. Use one if you want animations and focus styling out of the box. Writing it yourself is about 50 lines and avoids a dependency for one screen.

Can Android read the SMS code automatically without the user tapping?

Yes, with the SMS Retriever API, which needs your app hash in the SMS body and a native module, so it requires a development build. The keyboard suggestion from autoComplete="sms-otp" is simpler and is enough for most apps.

Does the OTP input work in Expo Go?

Yes. The pattern in this guide uses only core TextInput and Pressable. Only the Android SMS Retriever approach needs a native module and a development build.

Can an AI app builder generate the phone login flow?

Yes. Describe it, "phone sign-in with a 6-digit SMS code, resend after 30 seconds, using Supabase auth", and ShipNative generates the phone screen, the OTP input with autofill, and the verify call as real React Native.

→

React Native TextInput

The keyboard, autofill, and focus props this component builds on.

Read guide →
→

React Native Authentication

Where the code screen fits in a full sign-in flow.

Read guide →

Ship a real React Native app today

Describe, preview, and export Expo code, free to start.

Build with ShipNative
ShipNative

Build mobile apps with AI. Describe it, try it on your phone, own the code.

Features

Text to App AIApp Generator from ScreenshotPRD to Mobile App

Tools

All free toolsApp Cost CalculatorApp Name GeneratorApp Store Keyword ToolReact Native Components

Blog

All blog postsHow to Build an App Without CodingBest AI Tools for Real Mobile AppsExpo EAS App Store ChecklistLovable, Cursor & v0 for MobileBest AI App Builders in 2026React Native AI App Builder Guide
© 2026 ShipNative
PrivacyTermsAboutFAQPricingDiscordShipNative - Featured on Startup FameFeatured on Twelve ToolsFeatured on TinyShelfShipNative - Launched on Fazier