Skip to main content
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}
/>

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
  }}
/>

Platform-Specific Behavior

TextInput has different behaviors on iOS and Android, as seen in the React Native source:
// 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

Complete Login Form Example

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>
  );
}

Auto-advancing Forms

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>
  );
}

Input Events

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

1

Set Return Key Type

<TextInput returnKeyType="next" />
<TextInput returnKeyType="done" />
<TextInput returnKeyType="search" />
2

Handle Submit

<TextInput
  returnKeyType="search"
  onSubmitEditing={handleSearch}
/>
3

Enable/Disable Return Key

<TextInput
  enablesReturnKeyAutomatically={true}
  returnKeyType="done"
/>

Common Patterns

Search Input

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

Use value and onChangeText props to keep input state in sync with React state.
Use keyboardType to show the right keyboard for the input type (email, phone, number, etc.).
Set autoCorrect={false} and autoCapitalize="none" for email and username inputs.
Style inputs differently for focus, error, and disabled states.
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

Build docs developers (and LLMs) love