Noções Básicas de Formulários
TextInput controlado, fluxo de foco e tipos de teclado de celular.
Busque em todas as páginas da documentação
TextInput controlado, fluxo de foco e tipos de teclado de celular.
Todos os exemplos abaixo usam primitivas nativas do React Native - sem bibliotecas de formulário. Crie um aplicativo TypeScript padrão Expo SDK 57 e substitua App.tsx para executar cada trecho.
npx create-expo-app@latest MyFormsApp --template blank-typescript
cd MyFormsApp
npx expo startFerramentas: Estes exemplos visam o Expo SDK 57 (
expo~57.0.4), React Native 0.86.0 e React 19.2.3.
No celular, TextInput é sempre controlado quando você se preocupa com validação, envio ou redefinição de campos. Espelhe o React web: mantenha o value no estado e atualize-o em onChangeText.
import { useState } from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
export default function App() {
const [name, setName] = useState("");
return (
<View style={styles.screen}>
<Text style={styles.label}>Nome de exibição</Text>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder="Alex Chen"
placeholderTextColor="#94a3b8"
/>
<Text style={styles.preview}>
{name.length === 0 ? "Digite para pré-visualizar" : `Olá, ${name}`}
</Text>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 8 },
label: { fontSize: 14, fontWeight: "600", color: "#334155" },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
preview: { fontSize: 15, color: "#64748b", marginTop: 4 },
});onChangeText recebe uma string - não um evento sintético como onChange da webvalue={name} torna o campo nativo um componente controlado; omita value apenas para protótipos totalmente não controladossetName("") de um botão ou após o envio bem-sucedidoRelacionado: react-hook-form em Mobile - menos re-renderizações quando muitos inputs compartilham uma tela
keyboardType seleciona o layout do teclado de software. Combine-o com autoCapitalize e autoCorrect para que os usuários obtenham as teclas corretas e o comportamento de autocorreção por campo.
import { useState, type ReactNode } from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
export default function App() {
const [email, setEmail] = useState("");
const [age, setAge] = useState("");
const [phone, setPhone] = useState("");
return (
<View style={styles.screen}>
<Field label="Email">
<TextInput
style={styles.input}
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
textContentType="emailAddress"
placeholder="voce@exemplo.com"
/>
</Field>
<Field label="Idade">
<TextInput
style={styles.input}
value={age}
onChangeText={setAge}
keyboardType="number-pad"
placeholder="25"
/>
</Field>
<Field label="Telefone">
<TextInput
style={styles.input}
value={phone}
onChangeText={setPhone}
keyboardType="phone-pad"
textContentType="telephoneNumber"
placeholder="(555) 010-2030"
/>
</Field>
</View>
);
}
function Field({ label, children }: { label: string; children: ReactNode }) {
return (
<View style={styles.field}>
<Text style={styles.label}>{label}</Text>
{children}
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 16 },
field: { gap: 6 },
label: { fontSize: 14, fontWeight: "600", color: "#334155" },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
});keyboardType: default, email-address, numeric, number-pad, phone-pad, decimal-pad, urlemail-address e url incluem @ e / - ainda defina autoCapitalize="none" porque os padrões de maiúsculas/minúsculas de frase são irritantes nesses camposnumber-pad e phone-pad não têm tecla Return - planeje o envio por meio de um botão na tela ou mova o foco com refstextContentType (iOS) e autoComplete (Android) melhoram o preenchimento automático de gerenciadores de senha e SMS - expanda no exemplo de senha abaixoRelacionado: Seletores, Data/Hora e Inputs Nativos - quando um seletor nativo supera a entrada de texto livre
returnKeyType renomeia a tecla de ação do teclado - "Next", "Done", "Go", "Search". Combine o rótulo com o que realmente acontece quando o usuário o toca.
import { useState } from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
export default function App() {
const [query, setQuery] = useState("");
const [note, setNote] = useState("");
return (
<View style={styles.screen}>
<Text style={styles.label}>Pesquisar</Text>
<TextInput
style={styles.input}
value={query}
onChangeText={setQuery}
returnKeyType="search"
onSubmitEditing={() => console.log("search:", query)}
placeholder="Encontrar artigos"
/>
<Text style={styles.label}>Bio</Text>
<TextInput
style={[styles.input, styles.multiline]}
value={note}
onChangeText={setNote}
multiline
returnKeyType="default"
blurOnSubmit={false}
placeholder="Uma pequena biografia - Return insere uma nova linha"
/>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 8 },
label: { fontSize: 14, fontWeight: "600", color: "#334155" },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
multiline: { minHeight: 96, textAlignVertical: "top" },
});next entre campos em um assistente, done no último campo de linha única, go ao enviar para um servidor, send em UIs de chatblurOnSubmit={false} para que a tecla de ação não feche o teclado inesperadamenteonSubmitEditing é acionado quando o usuário toca na tecla de ação - conecte-o para focar no próximo campo ou executar a lógica de envioreturnKeyType inconsistentemente em alguns teclados OEM - sempre forneça um botão de fallback na telaRelacionado: Fluxos de Múltiplas Etapas e Assistentes -
returnKeyType="next"entre os limites das etapas
blurOnSubmit controla se o toque na tecla de ação fecha o teclado após onSubmitEditing. Para formulários de vários campos, defina blurOnSubmit={false} em todos os campos, exceto no último, para que o foco possa se mover sem que o teclado colapse.
import { useRef, useState } from "react";
import { StyleSheet, Text, TextInput, View, type TextInput as TextInputType } from "react-native";
export default function App() {
const [city, setCity] = useState("");
const [zip, setZip] = useState("");
const zipRef = useRef<TextInputType>(null);
return (
<View style={styles.screen}>
<Text style={styles.label}>Cidade</Text>
<TextInput
style={styles.input}
value={city}
onChangeText={setCity}
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => zipRef.current?.focus()}
placeholder="Portland"
/>
<Text style={styles.label}>CEP</Text>
<TextInput
ref={zipRef}
style={styles.input}
value={zip}
onChangeText={setZip}
keyboardType="number-pad"
returnKeyType="done"
blurOnSubmit
placeholder="97201"
/>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 8 },
label: { fontSize: 14, fontWeight: "600", color: "#334155" },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
});true para campos de linha única - bom para uma barra de pesquisa de um campo, errado para cadeias de "Next"blurOnSubmit={false} + onSubmitEditing → nextRef.focus() é o padrão de dois camposblurOnSubmit no padrão (true) ou defina-o explicitamente para que Done feche o tecladoblurOnSubmit importa menos lá; confie em um botão Enviar visívelRelacionado: Controlador de Teclado - mantenha os inputs visíveis quando o teclado estiver aberto
Anexe uma ref a TextInput para chamar .focus(), .blur() ou .clear() imperativamente. Use isso para aut Foco na montagem, saltar para o primeiro campo inválido ou atalhos de "Editar".
import { useEffect, useRef, useState } from "react";
import { Pressable, StyleSheet, Text, TextInput, View, type TextInput as TextInputType } from "react-native";
export default function App() {
const inputRef = useRef<TextInputType>(null);
const [code, setCode] = useState("");
useEffect(() => {
const timer = setTimeout(() => inputRef.current?.focus(), 300);
return () => clearTimeout(timer);
}, []);
return (
<View style={styles.screen}>
<Text style={styles.heading}>Digite o código de verificação</Text>
<TextInput
ref={inputRef}
style={styles.input}
value={code}
onChangeText={setCode}
keyboardType="number-pad"
maxLength={6}
placeholder="000000"
/>
<Pressable style={styles.button} onPress={() => inputRef.current?.focus()}>
<Text style={styles.buttonText}>Focar no input</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 12 },
heading: { fontSize: 20, fontWeight: "700", color: "#0f172a" },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 24,
letterSpacing: 8,
textAlign: "center",
backgroundColor: "#fff",
},
button: {
alignSelf: "flex-start",
backgroundColor: "#2563eb",
paddingHorizontal: 16,
paddingVertical: 10,
borderRadius: 8,
},
buttonText: { color: "#fff", fontWeight: "600" },
});useRef<TextInput>(null) - chame ref.current?.focus() com encadeamento opcional porque o nó pode ainda não estar montadosetTimeout ~300ms) após transições de navegação para que o teclado não lute contra animações de telafirstErrorRef.current?.focus() traz leitores de tela e usuários videntes para o campo do problemaref={inputRef} em TextInput é o padrão idiomáticoRelacionado: Acessibilidade em Formulários - anunciando erros e gerenciando o foco do VoiceOver/TalkBack
Encadeie campos combinando returnKeyType="next", blurOnSubmit={false}, refs e onSubmitEditing. A tecla Next do teclado deve pousar no próximo campo lógico todas as vezes.
import { useRef, useState } from "react";
import { StyleSheet, Text, TextInput, View, type TextInput as TextInputType } from "react-native";
export default function App() {
const [first, setFirst] = useState("");
const [last, setLast] = useState("");
const [company, setCompany] = useState("");
const lastRef = useRef<TextInputType>(null);
const companyRef = useRef<TextInputType>(null);
return (
<View style={styles.screen}>
<Text style={styles.label}>Primeiro nome</Text>
<TextInput
style={styles.input}
value={first}
onChangeText={setFirst}
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => lastRef.current?.focus()}
textContentType="givenName"
autoComplete="given-name"
placeholder="Alex"
/>
<Text style={styles.label}>Último nome</Text>
<TextInput
ref={lastRef}
style={styles.input}
value={last}
onChangeText={setLast}
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => companyRef.current?.focus()}
textContentType="familyName"
autoComplete="family-name"
placeholder="Chen"
/>
<Text style={styles.label}>Empresa</Text>
<TextInput
ref={companyRef}
style={styles.input}
value={company}
onChangeText={setCompany}
returnKeyType="done"
blurOnSubmit
textContentType="organizationName"
placeholder="Acme Inc."
/>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 8 },
label: { fontSize: 14, fontWeight: "600", color: "#334155" },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
});textContentType / autoComplete ajudam o preenchimento automático do SO a inserir valores sem quebrar sua cadeia de focoreturnKeyType="done" e chame seu manipulador de envio em onSubmitEditingScrollView - role o campo focado para a visualização antes de chamar .focus() em campos inferiores (veja o exemplo 9)Relacionado: Melhores Práticas de Formulários - reduzindo toques e prevenindo perda de dados na navegação de volta
Conecte um botão Enviar na tela para validar o estado, chamar sua API e fechar o teclado. Use keyboardShouldPersistTaps em scrollables pais para que o botão responda no primeiro toque.
import { useState } from "react";
import {
Alert,
Keyboard,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from "react-native";
export default function App() {
const [email, setEmail] = useState("");
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
if (!email.includes("@")) {
Alert.alert("Email inválido", "Digite um endereço de email válido.");
return;
}
setSubmitting(true);
Keyboard.dismiss();
try {
await fakeSubscribe(email);
Alert.alert("Inscrito", `Confirmação enviada para ${email}`);
setEmail("");
} finally {
setSubmitting(false);
}
}
return (
<ScrollView
contentContainerStyle={styles.screen}
keyboardShouldPersistTaps="handled"
>
<Text style={styles.heading}>Newsletter</Text>
<TextInput
style={styles.input}
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
returnKeyType="go"
onSubmitEditing={handleSubmit}
placeholder="voce@exemplo.com"
/>
<Pressable
style={[styles.button, submitting && styles.buttonDisabled]}
onPress={handleSubmit}
disabled={submitting}
>
<Text style={styles.buttonText}>{submitting ? "Enviando…" : "Inscrever-se"}</Text>
</Pressable>
</ScrollView>
);
}
async function fakeSubscribe(_email: string) {
await new Promise((r) => setTimeout(r, 600));
}
const styles = StyleSheet.create({
screen: { padding: 16, gap: 12 },
heading: { fontSize: 22, fontWeight: "700", color: "#0f172a" },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
button: {
backgroundColor: "#2563eb",
paddingVertical: 14,
borderRadius: 10,
alignItems: "center",
},
buttonDisabled: { opacity: 0.6 },
buttonText: { color: "#fff", fontSize: 16, fontWeight: "600" },
});Keyboard.dismiss() no início do envio para que o teclado não cubra a UI de sucessokeyboardShouldPersistTaps="handled" permite que Pressable receba toques sem exigir um segundo toque para fechar o teclado primeirosubmitting - evita posts duplos em redes lentasincludes("@") por Zod ou seu esquema de API antes de enviar - consulte o guia de validaçãoRelacionado: Validação com Zod - erros baseados em esquema exibidos ao lado de cada campo
Campos de senha precisam de secureTextEntry, textContentType / autoComplete sensatos e, muitas vezes, um alternador de visibilidade - os usuários não podem revisar o texto mascarado.
import { useState } from "react";
import { Pressable, StyleSheet, Text, TextInput, View } from "react-native";
export default function App() {
const [password, setPassword] = useState("");
const [visible, setVisible] = useState(false);
return (
<View style={styles.screen}>
<Text style={styles.label}>Senha</Text>
<View style={styles.row}>
<TextInput
style={styles.input}
value={password}
onChangeText={setPassword}
secureTextEntry={!visible}
textContentType="password"
autoComplete="password"
autoCapitalize="none"
autoCorrect={false}
returnKeyType="done"
placeholder="••••••••"
/>
<Pressable
style={styles.toggle}
onPress={() => setVisible((v) => !v)}
accessibilityRole="button"
accessibilityLabel={visible ? "Ocultar senha" : "Mostrar senha"}
>
<Text style={styles.toggleText}>{visible ? "Ocultar" : "Mostrar"}</Text>
</Pressable>
</View>
<Text style={styles.hint}>Mínimo de 8 caracteres</Text>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 6 },
label: { fontSize: 14, fontWeight: "600", color: "#334155" },
row: { flexDirection: "row", alignItems: "center", gap: 8 },
input: {
flex: 1,
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
toggle: { paddingHorizontal: 8, paddingVertical: 10 },
toggleText: { color: "#2563eb", fontWeight: "600" },
hint: { fontSize: 13, color: "#64748b" },
});secureTextEntry mascara caracteres na tela e em algumas capturas de tela - ainda trate o valor como sensível em logstextContentType="password" e autoComplete="password" - use newPassword em fluxos de inscriçãosecureTextEntry - o valor do estado permanece o mesmoRelacionado: Acessibilidade em Formulários - rotulando campos seguros para tecnologia assistiva
Formulários de tela inteira precisam de um contêiner de rolagem, prevenção de teclado e tratamento de toque para que os campos na parte inferior permaneçam acessíveis e os botões possam ser tocados.
import { useState } from "react";
import {
KeyboardAvoidingView,
Platform,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from "react-native";
export default function App() {
const [subject, setSubject] = useState("");
const [orderId, setOrderId] = useState("");
const [details, setDetails] = useState("");
return (
<KeyboardAvoidingView
style={styles.flex}
behavior={Platform.OS === "ios" ? "padding" : undefined}
keyboardVerticalOffset={Platform.OS === "ios" ? 64 : 0}
>
<ScrollView
contentContainerStyle={styles.content}
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag"
>
<Text style={styles.heading}>Solicitação de suporte</Text>
<View style={styles.field}>
<Text style={styles.label}>Assunto</Text>
<TextInput
style={styles.input}
value={subject}
onChangeText={setSubject}
returnKeyType="next"
blurOnSubmit={false}
placeholder="Resumo breve"
/>
</View>
<View style={styles.field}>
<Text style={styles.label}>ID do Pedido</Text>
<TextInput
style={styles.input}
value={orderId}
onChangeText={setOrderId}
returnKeyType="next"
blurOnSubmit={false}
placeholder="PED-12345"
/>
</View>
<View style={styles.field}>
<Text style={styles.label}>Detalhes</Text>
<TextInput
style={[styles.input, styles.multiline]}
value={details}
onChangeText={setDetails}
multiline
blurOnSubmit={false}
placeholder="O que aconteceu?"
/>
</View>
<Pressable style={styles.button} onPress={() => {}}>
<Text style={styles.buttonText}>Enviar solicitação</Text>
</Pressable>
</ScrollView>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
flex: { flex: 1 },
content: { padding: 16, gap: 12, paddingBottom: 40 },
heading: { fontSize: 22, fontWeight: "700", marginBottom: 4 },
field: { gap: 6 },
label: { fontSize: 14, fontWeight: "600", color: "#334155" },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
multiline: { minHeight: 120, textAlignVertical: "top" },
button: {
marginTop: 8,
backgroundColor: "#2563eb",
paddingVertical: 14,
borderRadius: 10,
alignItems: "center",
},
buttonText: { color: "#fff", fontWeight: "600", fontSize: 16 },
});KeyboardAvoidingView com behavior="padding" no iOS eleva o conteúdo quando o teclado abre - ajuste keyboardVerticalOffset para a altura do seu cabeçalhokeyboardDismissMode="on-drag" permite que os usuários arrastem a visualização de rolagem para fechar o teclado - esperado em formulários longoskeyboardShouldPersistTaps="handled" é necessário quando os botões de envio estão dentro do mesmo ScrollView que os inputsreact-native-keyboard-controller - vinculado abaixoRelacionado: Controlador de Teclado -
KeyboardAwareScrollViewe offsets consistentes entre plataformas
Este exemplo combina inputs controlados, tipos de teclado, uma cadeia de foco, mascaramento de senha, validação e envio - o padrão base que a maioria dos aplicativos estende.
import { useRef, useState } from "react";
import {
Alert,
Keyboard,
KeyboardAvoidingView,
Platform,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
type TextInput as TextInputType,
} from "react-native";
export default function App() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [submitting, setSubmitting] = useState(false);
const passwordRef = useRef<TextInputType>(null);
async function handleSignIn() {
if (!email.includes("@")) {
Alert.alert("Verifique o email", "Digite um endereço de email válido.");
return;
}
if (password.length < 8) {
Alert.alert("Verifique a senha", "A senha deve ter pelo menos 8 caracteres.");
passwordRef.current?.focus();
return;
}
setSubmitting(true);
Keyboard.dismiss();
try {
await fakeSignIn(email, password);
Alert.alert("Logado", `Bem-vindo de volta, ${email}`);
} catch {
Alert.alert("Falha no login", "Verifique suas credenciais e tente novamente.");
} finally {
setSubmitting(false);
}
}
return (
<KeyboardAvoidingView
style={styles.flex}
behavior={Platform.OS === "ios" ? "padding" : undefined}
>
<ScrollView
contentContainerStyle={styles.content}
keyboardShouldPersistTaps="handled"
>
<Text style={styles.heading}>Entrar</Text>
<Text style={styles.label}>Email</Text>
<TextInput
style={styles.input}
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
textContentType="username"
autoComplete="email"
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => passwordRef.current?.focus()}
placeholder="voce@exemplo.com"
/>
<Text style={styles.label}>Senha</Text>
<TextInput
ref={passwordRef}
style={styles.input}
value={password}
onChangeText={setPassword}
secureTextEntry
textContentType="password"
autoComplete="password"
returnKeyType="go"
onSubmitEditing={handleSignIn}
placeholder="••••••••"
/>
<Pressable
style={[styles.button, submitting && styles.buttonDisabled]}
onPress={handleSignIn}
disabled={submitting}
>
<Text style={styles.buttonText}>{submitting ? "Entrando…" : "Entrar"}</Text>
</Pressable>
</ScrollView>
</KeyboardAvoidingView>
);
}
async function fakeSignIn(_email: string, _password: string) {
await new Promise((r) => setTimeout(r, 800));
}
const styles = StyleSheet.create({
flex: { flex: 1 },
content: { padding: 16, gap: 8, paddingBottom: 32 },
heading: { fontSize: 28, fontWeight: "700", color: "#0f172a", marginBottom: 8 },
label: { fontSize: 14, fontWeight: "600", color: "#334155", marginTop: 4 },
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#fff",
},
button: {
marginTop: 16,
backgroundColor: "#2563eb",
paddingVertical: 14,
borderRadius: 10,
alignItems: "center",
},
buttonDisabled: { opacity: 0.6 },
buttonText: { color: "#fff", fontSize: 16, fontWeight: "600" },
});passwordRef.current?.focus() para que o usuário aterre no campo que precisa de atençãoreturnKeyType="next" no email e go na senha espelham a ação primária na telaKeyboard.dismiss() antes do trabalho assíncrono evita que o teclado reapareça sob overlays de carregamentoAlert por texto de erro inline sob cada campo - Zod + react-hook-form tornam isso escalável em formulários maioresRelacionado: react-hook-form em Mobile - a mesma UX com menos estado manual | Validação com Zod - esquemas reutilizáveis para regras de email e senha
Versões da Stack: Esta página foi escrita para React 19.2.3, React Native 0.86.0 e Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026