TextInput
By the end of this lesson you will be able to collect text from users with TextInput, show the right keyboard for each field, hide passwords, and move focus from one field to the next. These are the skills behind every login, search and checkout screen.
Your first TextInput
TextInput is React Native's text box. Like an <input> in web React, you usually make it controlled: the value lives in state, and every change updates that state.
There is one small difference. On the web you read event.target.value inside onChange. React Native gives you a simpler prop, onChangeText, which receives the new text directly.
Step 1: a name field that greets the user as they type.
import { useState } from "react";
import { View, Text, TextInput, StyleSheet } from "react-native";
export default function Greeting() {
const [name, setName] = useState("");
return (
<View style={styles.screen}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder="Your name"
placeholderTextColor="#888"
/>
<Text style={styles.hello}>Namaste, {name || "friend"}!</Text>
</View>
);
}
const styles = StyleSheet.create({
screen: { padding: 24, gap: 12 },
input: { borderWidth: 1, borderColor: "#bbb", borderRadius: 8, padding: 12, fontSize: 16 },
hello: { fontSize: 18 },
});What you should see: a bordered box with grey placeholder text. Tap it and the keyboard slides up. As you type "Ananya", the line below changes to "Namaste, Ananya!" letter by letter.
Choosing the right keyboard
A phone can show many keyboards: letters, numbers only, a phone dial pad, or one with an @ key. Showing the right one saves the user time. It is like a ticket counter that hands you a form with only the boxes you need.
| Field | Prop to use | Keyboard shown |
|---|---|---|
| Mobile number | inputMode="tel" | Phone dial pad |
| Amount in rupees | inputMode="decimal" | Numbers with a decimal point |
| PIN code | inputMode="numeric" | Numbers only |
inputMode="email" | Letters with @ and . | |
| Search | inputMode="search" | Letters with a search key |
inputMode is the newer, web-like prop. Older code uses keyboardType, with values such as "phone-pad", "number-pad" and "email-address". Both work; if you set both, inputMode wins.
Passwords, autofill and capitals
Step 2: build a login form with sensible settings for each field.
import { useState } from "react";
import { View, TextInput } from "react-native";
export default function LoginFields() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
return (
<View style={{ padding: 24, gap: 12 }}>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Email"
inputMode="email"
autoCapitalize="none"
autoCorrect={false}
autoComplete="email"
style={{ borderWidth: 1, padding: 12, borderRadius: 8 }}
/>
<TextInput
value={password}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry
autoComplete="password"
style={{ borderWidth: 1, padding: 12, borderRadius: 8 }}
/>
</View>
);
}What you should see: the email field shows an email keyboard and does not capitalise the first letter. The password field shows dots instead of letters. The phone may also offer saved emails and passwords above the keyboard.
autoCapitalize="none"stops "Priya@gmail.com" when the user meant "priya@gmail.com". Other values are"sentences"(the default),"words"and"characters".autoCorrect={false}stops the phone "fixing" email addresses into real words.autoCompletetells the system what the field is for, so it can offer autofill. For an SMS code field,autoComplete="one-time-code"helps the phone suggest the code.
Moving from one field to the next
On a long form, users want the keyboard's "Next" key to jump to the next box. You do this with a ref, a way to reach a component directly (see Refs & the DOM in the React course).
Step 3: link two fields together.
import { useRef, useState } from "react";
import { View, TextInput } from "react-native";
export default function Address() {
const cityRef = useRef(null);
const [street, setStreet] = useState("");
const [city, setCity] = useState("");
return (
<View style={{ padding: 24, gap: 12 }}>
<TextInput
value={street}
onChangeText={setStreet}
placeholder="House and street"
returnKeyType="next"
onSubmitEditing={() => cityRef.current?.focus()}
style={{ borderWidth: 1, padding: 12 }}
/>
<TextInput
ref={cityRef}
value={city}
onChangeText={setCity}
placeholder="City"
returnKeyType="done"
style={{ borderWidth: 1, padding: 12 }}
/>
</View>
);
}What you should see: the first keyboard shows a "Next" key. Pressing it moves the cursor into the City field, whose keyboard shows "Done".
For longer notes, add multiline. The box then grows or scrolls, and the return key adds a new line. Use maxLength={200} to limit the number of characters.
Practise the logic: a clean mobile number
Users paste numbers with spaces, dashes or "+91". A good input cleans the text as it arrives. The cleaning and checking is plain JavaScript inside a state update, so try it in the browser. In your app, onChange={(e) => handle(e.target.value)} becomes onChangeText={handle}.
import { useState } from "react";
import { createRoot } from "react-dom/client";
function cleanMobile(text) {
let digits = text.replace(/\D/g, "");
if (digits.length > 10 && digits.startsWith("91")) {
digits = digits.slice(2);
}
return digits.slice(0, 10);
}
function isValidMobile(digits) {
return /^[6-9]\d{9}$/.test(digits);
}
function MobileField() {
const [mobile, setMobile] = useState("");
function handle(text) {
setMobile(cleanMobile(text));
}
return (
<div style={{ fontFamily: "sans-serif" }}>
<input value={mobile} onChange={(e) => handle(e.target.value)} placeholder="Mobile number" />
<p>{mobile.length === 0 ? "Enter your number" : isValidMobile(mobile) ? "Looks good" : "Enter 10 digits starting with 6-9"}</p>
</div>
);
}
console.log(cleanMobile("+91 98765-43210"));
console.log(isValidMobile("9876543210"), isValidMobile("1234567890"));
createRoot(document.getElementById("root")).render(<MobileField />);9876543210 true false
Paste "+91 98765-43210" into the box. It becomes "9876543210" and the message says "Looks good".
Common mistakes
- Using
onChangeande.target.value. In React Native, useonChangeText, which gives you the string. - Invisible inputs on iOS. Add a border or background.
- Capitalised emails. Set
autoCapitalize="none"andautoCorrect={false}on email and username fields. - Number fields that return numbers. A
TextInputalways gives you a string. Convert withNumber(text)when you calculate. - Forgetting
value. Withoutvalue, the input is uncontrolled and you cannot clean or reset it from state.
Key points
TextInputis the text box; make it controlled withvalueandonChangeText.inputMode(or the olderkeyboardType) picks the right keyboard.secureTextEntryhides passwords;autoCompleteenables autofill.autoCapitalizeandautoCorrectcontrol automatic text changes.- Use a ref with
onSubmitEditingandfocus()to move to the next field. - The value is always a string, so convert it before doing maths.
Exercise
Build a "UPI payment" form with two fields: UPI ID and amount. The UPI ID field should not auto-capitalise or auto-correct. The amount field should show a decimal keyboard and accept only digits and one decimal point. The "Next" key on the UPI field should move to the amount. Below the fields, show "Pay ₹AMOUNT to UPI_ID" once both have values.
Show solution
import { useRef, useState } from "react";
import { View, Text, TextInput } from "react-native";
export default function PayForm() {
const amountRef = useRef(null);
const [upi, setUpi] = useState("");
const [amount, setAmount] = useState("");
function onAmount(text) {
const cleaned = text.replace(/[^0-9.]/g, "");
const parts = cleaned.split(".");
setAmount(parts.length > 2 ? parts[0] + "." + parts.slice(1).join("") : cleaned);
}
return (
<View style={{ padding: 24, gap: 12 }}>
<TextInput value={upi} onChangeText={setUpi} placeholder="name@bank"
autoCapitalize="none" autoCorrect={false} returnKeyType="next"
onSubmitEditing={() => amountRef.current?.focus()} style={{ borderWidth: 1, padding: 12 }} />
<TextInput ref={amountRef} value={amount} onChangeText={onAmount} placeholder="Amount"
inputMode="decimal" style={{ borderWidth: 1, padding: 12 }} />
{upi && amount ? <Text>Pay ₹{amount} to {upi}</Text> : null}
</View>
);
}onAmount removes anything that is not a digit or a dot, then joins extra dots away so only one decimal point remains.
Quick Quiz
1. Which prop gives you the new text directly as a string?
onChangeText receives the updated string, so you can pass a setter straight to it.
2. Which prop hides the characters of a password?
secureTextEntry shows dots instead of the typed characters.
3. What type of value does a TextInput give you when the user types 250?
Text inputs always produce strings; convert them with Number() before maths.
4. How do you move the cursor to the next field when the user presses "Next"?
A ref gives you the next input, and onSubmitEditing runs when the return key is pressed.
Learn React Native with a mentor
Free lessons get you started. Our Mobile App Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
