The TextInput component is the fundamental way to get text input from users in React Native. This guide covers everything you need to know about handling text input.
Basic TextInput
The simplest form of text input:
import { View , TextInput } from 'react-native' ;
import { useState } from 'react' ;
function SimpleInput () {
const [ text , setText ] = useState ( '' );
return (
< View >
< TextInput
value = { text }
onChangeText = { setText }
placeholder = "Type here..."
style = { {
height: 40 ,
borderWidth: 1 ,
borderColor: '#ccc' ,
paddingHorizontal: 8 ,
} }
/>
</ View >
);
}
Always control your TextInput with value and onChangeText. This makes it a “controlled component” that React can manage.
Common TextInput Props
Keyboard Types
Different keyboards for different input types:
< TextInput
placeholder = "Email"
keyboardType = "email-address"
autoCapitalize = "none"
autoCorrect = { false }
/>
< TextInput
placeholder = "Phone"
keyboardType = "phone-pad"
/>
< TextInput
placeholder = "Amount"
keyboardType = "numeric"
/>
< TextInput
placeholder = "Website"
keyboardType = "url"
autoCapitalize = "none"
/>
Secure Text Entry
For passwords and sensitive data:
import { TextInput , View , Text } from 'react-native' ;
import { useState } from 'react' ;
function PasswordInput () {
const [ password , setPassword ] = useState ( '' );
const [ showPassword , setShowPassword ] = useState ( false );
return (
< View >
< TextInput
value = { password }
onChangeText = { setPassword }
placeholder = "Password"
secureTextEntry = { ! showPassword }
style = { {
height: 40 ,
borderWidth: 1 ,
paddingHorizontal: 8 ,
} }
/>
</ View >
);
}
Multiline Text
For longer text input:
< TextInput
multiline
numberOfLines = { 4 }
placeholder = "Enter your message..."
style = { {
height: 100 ,
borderWidth: 1 ,
padding: 8 ,
textAlignVertical: 'top' , // Android
} }
/>
TextInput has different behaviors on iOS and Android, as seen in the React Native source:
iOS Implementation
Android Implementation
// From TextInput.js source
if ( Platform . OS === 'ios' ) {
RCTSinglelineTextInputView =
require ( './RCTSingelineTextInputNativeComponent' ). default ;
RCTMultilineTextInputView =
require ( './RCTMultilineTextInputNativeComponent' ). default ;
}
Uses UITextField for single-line
Uses UITextView for multiline
returnKeyType affects keyboard appearance
clearButtonMode shows clear button
Uses EditText component
returnKeyType called returnKeyLabel internally
underlineColorAndroid for bottom border color
textAlignVertical for multiline alignment
Real-world form with validation:
import { View , TextInput , Button , Text , StyleSheet , Alert } from 'react-native' ;
import { useState } from 'react' ;
function LoginForm () {
const [ email , setEmail ] = useState ( '' );
const [ password , setPassword ] = useState ( '' );
const [ errors , setErrors ] = useState ({});
const validateEmail = ( email ) => {
const re = / ^ [ ^ \s@ ] + @ [ ^ \s@ ] + \. [ ^ \s@ ] + $ / ;
return re . test ( email );
};
const handleLogin = () => {
const newErrors = {};
if ( ! email ) {
newErrors . email = 'Email is required' ;
} else if ( ! validateEmail ( email )) {
newErrors . email = 'Invalid email format' ;
}
if ( ! password ) {
newErrors . password = 'Password is required' ;
} else if ( password . length < 6 ) {
newErrors . password = 'Password must be at least 6 characters' ;
}
if ( Object . keys ( newErrors ). length > 0 ) {
setErrors ( newErrors );
return ;
}
// Clear errors and proceed
setErrors ({});
Alert . alert ( 'Success' , 'Logging in...' );
};
return (
< View style = { styles . container } >
< Text style = { styles . title } > Login </ Text >
< View style = { styles . inputContainer } >
< TextInput
style = { [ styles . input , errors . email && styles . inputError ] }
placeholder = "Email"
value = { email }
onChangeText = { ( text ) => {
setEmail ( text );
if ( errors . email ) {
setErrors ({ ... errors , email: null });
}
} }
keyboardType = "email-address"
autoCapitalize = "none"
autoCorrect = { false }
/>
{ errors . email && < Text style = { styles . errorText } > { errors . email } </ Text > }
</ View >
< View style = { styles . inputContainer } >
< TextInput
style = { [ styles . input , errors . password && styles . inputError ] }
placeholder = "Password"
value = { password }
onChangeText = { ( text ) => {
setPassword ( text );
if ( errors . password ) {
setErrors ({ ... errors , password: null });
}
} }
secureTextEntry
/>
{ errors . password && (
< Text style = { styles . errorText } > { errors . password } </ Text >
) }
</ View >
< Button title = "Login" onPress = { handleLogin } />
</ View >
);
}
const styles = StyleSheet . create ({
container: {
padding: 20 ,
},
title: {
fontSize: 24 ,
fontWeight: 'bold' ,
marginBottom: 20 ,
textAlign: 'center' ,
},
inputContainer: {
marginBottom: 15 ,
},
input: {
height: 40 ,
borderWidth: 1 ,
borderColor: '#ddd' ,
borderRadius: 5 ,
paddingHorizontal: 10 ,
backgroundColor: '#fff' ,
},
inputError: {
borderColor: 'red' ,
},
errorText: {
color: 'red' ,
fontSize: 12 ,
marginTop: 5 ,
},
});
export default LoginForm ;
Advanced Features
Managing Focus
import { TextInput , Button , View } from 'react-native' ;
import { useRef } from 'react' ;
function FocusExample () {
const inputRef = useRef ( null );
return (
< View >
< TextInput
ref = { inputRef }
placeholder = "Input"
style = { { height: 40 , borderWidth: 1 } }
/>
< Button
title = "Focus Input"
onPress = { () => inputRef . current . focus () }
/>
< Button
title = "Blur Input"
onPress = { () => inputRef . current . blur () }
/>
</ View >
);
}
import { View , TextInput } from 'react-native' ;
import { useRef , useState } from 'react' ;
function PinInput () {
const [ pin , setPin ] = useState ([ '' , '' , '' , '' ]);
const refs = [ useRef ( null ), useRef ( null ), useRef ( null ), useRef ( null )];
const handleChange = ( text , index ) => {
const newPin = [ ... pin ];
newPin [ index ] = text ;
setPin ( newPin );
// Auto-advance to next input
if ( text && index < 3 ) {
refs [ index + 1 ]. current . focus ();
}
};
return (
< View style = { { flexDirection: 'row' , gap: 10 } } >
{ pin . map (( digit , index ) => (
< TextInput
key = { index }
ref = { refs [ index ] }
value = { digit }
onChangeText = { ( text ) => handleChange ( text , index ) }
keyboardType = "number-pad"
maxLength = { 1 }
style = { {
width: 50 ,
height: 50 ,
borderWidth: 1 ,
textAlign: 'center' ,
fontSize: 20 ,
} }
/>
)) }
</ View >
);
}
TextInput provides several event handlers:
< TextInput
onFocus = { () => console . log ( 'Input focused' ) }
onBlur = { () => console . log ( 'Input blurred' ) }
onChangeText = { ( text ) => console . log ( 'Text changed:' , text ) }
onSubmitEditing = { () => console . log ( 'Submit pressed' ) }
onKeyPress = { ({ nativeEvent }) => console . log ( 'Key:' , nativeEvent . key ) }
/>
Keyboard Configuration
Set Return Key Type
< TextInput returnKeyType = "next" />
< TextInput returnKeyType = "done" />
< TextInput returnKeyType = "search" />
Handle Submit
< TextInput
returnKeyType = "search"
onSubmitEditing = { handleSearch }
/>
Enable/Disable Return Key
< TextInput
enablesReturnKeyAutomatically = { true }
returnKeyType = "done"
/>
Common Patterns
import { TextInput , View , Text } from 'react-native' ;
import { useState , useEffect } from 'react' ;
function SearchInput () {
const [ query , setQuery ] = useState ( '' );
const [ results , setResults ] = useState ([]);
useEffect (() => {
if ( query . length > 2 ) {
// Perform search
fetch ( `https://api.example.com/search?q= ${ query } ` )
. then ( res => res . json ())
. then ( data => setResults ( data ));
}
}, [ query ]);
return (
< View >
< TextInput
placeholder = "Search..."
value = { query }
onChangeText = { setQuery }
autoCorrect = { false }
clearButtonMode = "while-editing"
/>
{ results . map ( item => (
< Text key = { item . id } > { item . name } </ Text >
)) }
</ View >
);
}
Best Practices
Always control your inputs
Use value and onChangeText props to keep input state in sync with React state.
Set appropriate keyboard types
Use keyboardType to show the right keyboard for the input type (email, phone, number, etc.).
Disable auto-correct for emails and usernames
Set autoCorrect={false} and autoCapitalize="none" for email and username inputs.
Provide clear visual feedback
Style inputs differently for focus, error, and disabled states.
Use secureTextEntry for passwords
Never display passwords in plain text.
Next Steps
Using Lists Learn to render search results and data lists
Platform-Specific Code Handle iOS and Android differences