Almost every mobile app needs to collect something from the user: a username, a password, a search query, a message, or a payment detail.
In React Native, all of that starts with one core component: TextInput.
This guide to React Native text input walks you through everything you need: the basics of the TextInput component, managing its value with state, handling events, customizing the keyboard, validating input, building forms, and avoiding common mistakes.
Every section includes code you can copy into your own project.
What Is the TextInput Component in React Native?
TextInput is a built-in React Native component that lets users type text into your app using the device keyboard. It's the mobile equivalent of the HTML <input> and <textarea> elements, and it maps to native text fields on both iOS and Android, so it feels natural on each platform.
You import it directly from react-native:
import { TextInput } from "react-native";Basic Usage: Your First TextInput
Here is the simplest possible example:
import React from "react";
import { View, TextInput, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Type something..."
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: "center", padding: 20 },
input: {
height: 44,
borderWidth: 1,
borderColor: "#ccc",
borderRadius: 8,
paddingHorizontal: 12,
},
});By default, a TextInput has no border, background, or padding. You need to style it yourself, and it's a good idea to set an explicit height so the field is comfortable to tap.
Managing Input Value with State
To read what the user types, store the value in React state and update it with the onChangeText prop. This pattern is called a controlled component.
import React, { useState } from "react";
import { View, TextInput, Text } from "react-native";
export default function NameInput() {
const [name, setName] = useState("");
return (
<View style={{ padding: 20 }}>
<TextInput
value={name}
onChangeText={setName}
placeholder="Enter your name"
style={{ borderWidth: 1, padding: 10, borderRadius: 8 }}
/>
<Text>Hello, {name || "stranger"}!</Text>
</View>
);
}Unlike the web, onChangeText gives you the text string directly, not an event object. That makes simple cases cleaner. If you need more detail about the change, use onChange, which provides a native event.
Essential TextInput Props
The TextInput component has many props. These are the ones you will use most often:
Prop | What it does |
|---|---|
| The current text shown in the field (for controlled inputs) |
| Called with the new text every time it changes |
| Hint text shown when the field is empty |
| Color of the placeholder text |
| Hides the text, ideal for passwords |
| Chooses which keyboard layout appears |
| Controls automatic capitalization |
| Turns spell correction on or off |
| Limits the number of characters |
| Allows multiple lines of text |
| Enables or disables typing |
| Changes the label of the keyboard's return key |
Choosing the Right Keyboard Type
One of the easiest ways to improve user experience is to show the right keyboard. Use the keyboardType prop to match the data you want.
<TextInput keyboardType="email-address" autoCapitalize="none" placeholder="Email" />
<TextInput keyboardType="numeric" placeholder="Age" />
<TextInput keyboardType="phone-pad" placeholder="Phone number" />
<TextInput keyboardType="url" autoCapitalize="none" placeholder="Website" />Common values include default, numeric, email-address, phone-pad, number-pad, decimal-pad, and url. A few options are platform-specific, so test on both iOS and Android.
Handling Passwords and Sensitive Input
For passwords, set secureTextEntry to hide characters. You can also pair it with a toggle so users can reveal what they typed.
import React, { useState } from "react";
import { View, TextInput, Button } from "react-native";
export default function PasswordField() {
const [password, setPassword] = useState("");
const [hidden, setHidden] = useState(true);
return (
<View>
<TextInput
value={password}
onChangeText={setPassword}
secureTextEntry={hidden}
autoCapitalize="none"
autoCorrect={false}
placeholder="Password"
style={{ borderWidth: 1, padding: 10, borderRadius: 8 }}
/>
<Button
title={hidden ? "Show" : "Hide"}
onPress={() => setHidden(!hidden)}
/>
</View>
);
}Also consider the textContentType (iOS) and autoComplete (Android) props so password managers and autofill work properly.
Handling Input Events
Besides onChangeText, TextInput offers several events that let you respond to what the user is doing:
onFocus: Fires when the field is tapped and gains focus.onBlur: Fires when the field loses focus. This is a great time to validate.onSubmitEditing: Fires when the user presses the keyboard's submit or return key.onEndEditing: Fires when the user finishes editing.onKeyPress: Fires on each key press, useful for special key handling.
<TextInput
value={query}
onChangeText={setQuery}
onFocus={() => console.log("Focused")}
onBlur={() => console.log("Blurred")}
onSubmitEditing={() => search(query)}
returnKeyType="search"
/>Multiline Text Input
For comments, bios, or messages, enable multiline and control the height with numberOfLines or styling.
<TextInput
multiline
numberOfLines={4}
value={bio}
onChangeText={setBio}
placeholder="Tell us about yourself"
textAlignVertical="top"
style={{ height: 100, borderWidth: 1, padding: 10, borderRadius: 8 }}
/>On Android, multiline fields center their text vertically by default, so add textAlignVertical="top" for the expected behavior.
Moving Focus Between Inputs with Refs
In a form, users expect the next field to be focused when they press the return key. You can do that with useRef.
import React, { useRef, useState } from "react";
import { View, TextInput } from "react-native";
export default function LoginForm() {
const passwordRef = useRef(null);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
return (
<View>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Email"
keyboardType="email-address"
autoCapitalize="none"
returnKeyType="next"
onSubmitEditing={() => passwordRef.current?.focus()}
/>
<TextInput
ref={passwordRef}
value={password}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry
returnKeyType="done"
/>
</View>
);
}The ref also gives you methods like focus(), blur(), and clear(), which are handy for programmatic control.
Validating Text Input
Good validation tells users what went wrong without annoying them. A common approach is to validate on blur or on submit and store any error message in state.
import React, { useState } from "react";
import { View, TextInput, Text } from "react-native";
export default function EmailField() {
const [email, setEmail] = useState("");
const [error, setError] = useState("");
const validate = () => {
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
setError(valid ? "" : "Please enter a valid email address");
};
return (
<View>
<TextInput
value={email}
onChangeText={setEmail}
onBlur={validate}
keyboardType="email-address"
autoCapitalize="none"
placeholder="Email"
style={{
borderWidth: 1,
borderColor: error ? "red" : "#ccc",
padding: 10,
borderRadius: 8,
}}
/>
{error ? <Text style={{ color: "red" }}>{error}</Text> : null}
</View>
);
}For larger forms, consider a library such as React Hook Form together with a schema validator like Zod or Yup. They reduce boilerplate and keep validation logic organized.
Preventing the Keyboard from Covering Your Inputs
On mobile, the keyboard can hide the field being edited. Two built-in tools help:
KeyboardAvoidingView: Adjusts the layout so content moves above the keyboard.ScrollViewwithkeyboardShouldPersistTaps="handled": Lets users tap buttons while the keyboard is open instead of needing two taps.
import { KeyboardAvoidingView, Platform, ScrollView } from "react-native";
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : undefined}
>
<ScrollView keyboardShouldPersistTaps="handled">
{/* your inputs here */}
</ScrollView>
</KeyboardAvoidingView>To dismiss the keyboard when the user taps outside a field, wrap your screen in a Pressable or TouchableWithoutFeedback and call Keyboard.dismiss().
Building a Complete Form Example
Here is a small sign-up form that brings several concepts together:
import React, { useState } from "react";
import { View, TextInput, Button, Text, StyleSheet } from "react-native";
export default function SignUpForm() {
const [form, setForm] = useState({ name: "", email: "", password: "" });
const [submitted, setSubmitted] = useState(false);
const update = (field) => (value) =>
setForm((prev) => ({ ...prev, [field]: value }));
const canSubmit =
form.name.trim() && form.email.includes("@") && form.password.length >= 8;
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Full name"
value={form.name}
onChangeText={update("name")}
/>
<TextInput
style={styles.input}
placeholder="Email"
keyboardType="email-address"
autoCapitalize="none"
value={form.email}
onChangeText={update("email")}
/>
<TextInput
style={styles.input}
placeholder="Password (8+ characters)"
secureTextEntry
value={form.password}
onChangeText={update("password")}
/>
<Button
title="Sign up"
disabled={!canSubmit}
onPress={() => setSubmitted(true)}
/>
{submitted && <Text>Thanks for signing up, {form.name}!</Text>}
</View>
);
}
const styles = StyleSheet.create({
container: { padding: 20, gap: 12 },
input: { borderWidth: 1, borderColor: "#ccc", borderRadius: 8, padding: 10 },
});Storing the whole form in one state object keeps your code tidy, and the update helper avoids writing a separate handler for every field.
Best Practices for React Native Text Input
- Always use the right keyboard type. It speeds up entry and reduces mistakes.
- Set
autoCapitalize="none"for emails and usernames. - Provide clear labels, not just placeholders. Placeholders disappear as soon as the user types.
- Validate at the right time. Prefer on blur or on submit over every keystroke.
- Support autofill and password managers with
textContentTypeandautoComplete. - Make touch targets large enough. Aim for a height of at least 44 points.
- Handle the keyboard gracefully with
KeyboardAvoidingViewand scrollable layouts. - Add accessibility props such as
accessibilityLabelso screen readers describe each field. - Avoid heavy work inside
onChangeText. Debounce expensive actions like network searches.
Common Mistakes to Avoid
- Forgetting to pass
valuealong withonChangeText, which creates an uncontrolled input that's hard to reset - Using the web-style
onChangeand expectinge.target.value - Leaving
autoCorrecton for emails, usernames, and codes - Not testing on both iOS and Android, where keyboards and multiline behavior differ
- Triggering a network request on every keystroke without debouncing
- Letting the keyboard cover the submit button
Frequently Asked Questions
How do I get the value of a TextInput in React Native?
Store the text in state using useState and update it with the onChangeText prop. The state variable always holds the current value.
How do I clear a TextInput in React Native?
If the input is controlled, set its state back to an empty string. You can also call clear() on a ref attached to the input.
How do I make a TextInput accept only numbers?
Set keyboardType="numeric" to show a number keyboard, then sanitize the value in onChangeText, for example with text.replace(/[^0-9]/g, ""). The keyboard type alone does not guarantee only digits, especially with pasted text.
How do I hide the keyboard programmatically?
Import Keyboard from react-native and call Keyboard.dismiss().
What is the difference between onChange and onChangeText?
onChangeText passes the new text string directly. onChange passes a native event object with more details. Most apps use onChangeText for simplicity.
Should I use a form library in React Native?
For a couple of fields, plain state is fine. For longer forms with complex validation, a library like React Hook Form saves time and reduces bugs.



