Shipnative
ShipNativeShipNative
Sign inStart building
GuideOctober 2026 · 9 min read

React Native TextInput: Props, Keyboards, and Fixes

TextInput is the one form component React Native ships in core, and it looks simple until a user actually types into it. The wrong keyboard appears, the password manager ignores your login screen, the keyboard drops between fields, and on Android the box is taller than the design for no visible reason. This guide covers the basic controlled input, the exact props for each common field type, chaining fields with the return key, and the three bugs that show up only on a real device.

The basic controlled input

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

export default function NameField() {
  const [name, setName] = useState('');

  return (
    <View style={styles.wrap}>
      <Text style={styles.label}>Name</Text>
      <TextInput
        value={name}
        onChangeText={setName}
        placeholder="Ada Lovelace"
        placeholderTextColor="#5a5a5a"
        autoCapitalize="words"
        style={styles.input}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  wrap: { marginBottom: 20 },
  label: { color: '#8a8a8a', fontSize: 13, marginBottom: 8 },
  input: {
    height: 52,
    paddingHorizontal: 16,
    paddingVertical: 0,          // Android adds its own otherwise
    borderRadius: 12,
    backgroundColor: '#141414',
    borderWidth: 1,
    borderColor: 'rgba(255,255,255,0.08)',
    color: '#fff',
    fontSize: 16,
  },
});

Two details web developers miss. First, onChangeText gives you the string directly, so there is no e.target.value. Second, there is no default styling worth keeping: no border, no background, and a text colour that may be black on your dark screen. Always set color and placeholderTextColor explicitly, or the field is invisible in dark mode on one platform and fine on the other.

Keep fontSize at 16 or above for inputs. It reads better on a phone, and it matches what users expect from every other app on the device.

The right props for each field type

There is no type="email". The keyboard, autofill, and capitalisation are separate props, and getting them right is most of what makes a form feel native. Since React Native 0.71, inputMode is the cross-platform way to pick the keyboard and replaces most uses of keyboardType.

FieldPropsWhat it buys you
EmailinputMode="email" autoCapitalize="none" autoComplete="email" textContentType="emailAddress"Shows @ on the keyboard and stops the first letter being capitalised
Password (log in)secureTextEntry autoComplete="password" textContentType="password"Lets the OS password manager offer saved credentials
Password (sign up)secureTextEntry textContentType="newPassword"iOS offers a strong generated password instead of a saved one
PhoneinputMode="tel" autoComplete="tel" textContentType="telephoneNumber"Phone pad plus autofill from the contact card
AmountinputMode="decimal"Number pad with a decimal separator for the user locale
SMS codeinputMode="numeric" textContentType="oneTimeCode" autoComplete="sms-otp"iOS and Android suggest the code from the incoming text message
SearchinputMode="search" returnKeyType="search" autoCorrect={false}Return key reads "Search" and autocorrect stops rewriting queries

textContentType is iOS only and autoComplete drives Android autofill, which is why the login rows set both. Skip them and the form still works, but the password manager never offers to fill it, and that is the difference users notice on day one. The numeric keyboards are a hint, not a guarantee: paste and hardware keyboards ignore them, so still sanitise in onChangeText.

Moving to the next field with the return key

import { useRef, useState } from 'react';
import { TextInput } from 'react-native';

export default function LoginForm({ onSubmit }) {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const passwordRef = useRef<TextInput>(null);

  return (
    <>
      <TextInput
        value={email}
        onChangeText={setEmail}
        inputMode="email"
        autoCapitalize="none"
        autoComplete="email"
        textContentType="emailAddress"
        returnKeyType="next"
        submitBehavior="submit"   // keep the keyboard up
        onSubmitEditing={() => passwordRef.current?.focus()}
      />
      <TextInput
        ref={passwordRef}
        value={password}
        onChangeText={setPassword}
        secureTextEntry
        autoComplete="password"
        textContentType="password"
        returnKeyType="go"
        onSubmitEditing={() => onSubmit(email, password)}
      />
    </>
  );
}

Without submitBehavior="submit" the keyboard closes on return and reopens a beat later for the next field, which reads as a glitch. On React Native older than 0.73 the same fix is blurOnSubmit={false}. The last field uses returnKeyType="go" and submits, so a user can log in without ever reaching for the button.

Three bugs you only see on a real phone

  1. The Android box is too tall, or the text sits low. Android TextInput ships with default vertical padding and extra font padding. Set paddingVertical yourself and add includeFontPadding: false if the text still looks off-centre. For multiline inputs, add textAlignVertical: 'top' or the first line starts in the middle of the box. Multiline fields have their own quirks, covered in the text area guide.
  2. The cursor jumps to the end while typing. This happens when onChangeText rewrites the value, for example inserting spaces into a card number or dashes into a phone number. React Native sets the new string and the caret lands at the end, so editing the middle of the value becomes impossible. Format on blur, or show the formatted version in a separate Text and keep the input raw.
  3. Typing lags on a busy screen. A controlled input re-renders its owner on every keystroke. If the owner is a whole screen with a list and images, each letter re-renders all of it. Move the input and its state into a small component that reports upward on submit or blur, or use defaultValue and a ref for long free-text fields.

The fourth bug is not really a TextInput bug: the keyboard covers the field you are typing into. That one has its own fix in the KeyboardAvoidingView guide.

Should you use a form library?

For two or three fields, plain useStateis clearer than any library. Past about five fields, or once you have rules like “confirm password must match”, React Hook Form with a schema validator saves real code. It wraps TextInput through its Controller component and keeps re-renders scoped to the field that changed, which also solves the lag problem above. The form validation guide walks through that setup.

Keyboard behaviour is the one part of a form you cannot judge in a browser. If you want to see whether the right keyboards, autofill, and return-key chain are in place, describe the screen, “a sign-up form with name, email, phone, and password”, and ShipNative builds it as real React Native that runs on your phone, where you can tap through it with the actual keyboard.

Frequently Asked Questions

How do I get the value of a TextInput in React Native?

Store it in state and update it from onChangeText, which hands you the new string directly. Use onChange only if you need the native event object. There is no input.value to read later the way there is on the web, unless you keep the input uncontrolled and track the text yourself in onChangeText.

How do I make a number-only TextInput?

Set inputMode="numeric" (or "decimal" for amounts) to show the number keyboard, then still sanitise in onChangeText, because paste and hardware keyboards bypass the on-screen keyboard. The value is always a string, so parse it with Number() at the point you use it and keep the raw string in state so a half-typed "12." does not get eaten.

Why does my TextInput look taller on Android than on iOS?

Android TextInput has default vertical padding and extra font padding that iOS does not. Set paddingVertical explicitly in your style and, if text still sits off-centre, add includeFontPadding: false. Older Android versions also draw an underline, which underlineColorAndroid="transparent" removes.

How do I move to the next field when the user presses return?

Give the next input a ref, set returnKeyType="next" on the current one, and call nextRef.current?.focus() in onSubmitEditing. Add submitBehavior="submit" (or blurOnSubmit={false} on React Native older than 0.73) so the keyboard stays up between fields instead of dropping and popping back.

Should TextInput be controlled or uncontrolled?

Controlled (value plus onChangeText) is the default and right for most forms because validation and submit logic need the current value. Uncontrolled (defaultValue plus a ref) is worth it for long free-text fields in screens that re-render heavily, because it avoids pushing every keystroke back through React.

Can an AI app builder generate forms with the right keyboard types?

Yes. Describe the form, "sign-up with email, password, and phone", and ShipNative generates the TextInputs with matching inputMode, autofill, and return-key wiring as real React Native, then runs it on your phone, which is the only place keyboard behaviour can actually be checked.

→

React Native Form Validation

Inputs collect the values. This is how you check them.

Read guide →
→

React Native KeyboardAvoidingView

The keyboard will cover your input. Here is how to stop it.

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
PrivacyTermsFAQPricingDiscord