Capture user input with TextInput, respond to taps with Pressable, and keep the keyboard from covering your form — the building blocks of every interactive screen.
Why: TextInput is a controlled component — you hold its text in state and update it with onChangeText. Props like placeholder, keyboardType, secureTextEntry (for passwords), and autoCapitalize shape the input and the on-screen keyboard.
import { useState } from 'react'
import { TextInput } from 'react-native'
function EmailField() {
const [email, setEmail] = useState('')
return (
<TextInput
value={email}
onChangeText={setEmail}
placeholder="you@example.com"
keyboardType="email-address"
autoCapitalize="none"
style={{ borderWidth: 1, borderColor: '#ccc', padding: 12, borderRadius: 8 }}
/>
)
}Why: Pressable is the modern, flexible way to handle taps on anything — it reports the press state so you can give visual feedback. Wrap any component in it. The style prop can be a function receiving { pressed } to style the pressed state.
import { Pressable, Text } from 'react-native'
function SubmitButton({ onPress }: { onPress: () => void }) {
return (
<Pressable
onPress={onPress}
style={({ pressed }) => [
{ padding: 14, borderRadius: 8, backgroundColor: '#2DD4A0' },
pressed && { opacity: 0.6 },
]}
>
<Text style={{ color: 'white', textAlign: 'center' }}>Submit</Text>
</Pressable>
)
}Why: when the keyboard opens it can cover the input being typed into. KeyboardAvoidingView shifts content up so the focused field stays visible. behavior differs by platform — "padding" on iOS, "height" on Android is a common pairing.
import { KeyboardAvoidingView, Platform, TextInput } from 'react-native'
function Form() {
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<TextInput placeholder="Type here" />
</KeyboardAvoidingView>
)
}Why: putting it together — local state per field, a derived validity check, and a disabled submit until the form is valid. This is the everyday pattern; reach for a form library only when forms grow large.
function LoginForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const valid = email.includes('@') && password.length >= 8
return (
<View style={{ gap: 12, padding: 16 }}>
<TextInput value={email} onChangeText={setEmail} placeholder="Email" />
<TextInput
value={password}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry
/>
<Button title="Log in" disabled={!valid} onPress={() => {}} />
</View>
)
}