Fundamentos de formularios
TextInput controlados, flujo de enfoque y tipos de teclado móvil.
Busca en todas las páginas de la documentación
TextInput controlados, flujo de enfoque y tipos de teclado móvil.
Cada ejemplo a continuación utiliza primitivos de React Native integrados - sin librerías de formularios. Crea un app de Expo SDK 57 TypeScript estándar y reemplaza App.tsx para ejecutar cada fragmento.
npx create-expo-app@latest MyFormsApp --template blank-typescript
cd MyFormsApp
npx expo startHerramientas: Estos ejemplos están dirigidos a Expo SDK 57 (
expo~57.0.4), React Native 0.86.0 y React 19.2.3.
En dispositivos móviles, TextInput es siempre controlado cuando te interesa la validación, el envío o el reinicio de campos. Refleja web React: mantén value en estado y actualízalo en 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}>Nombre para mostrar</Text>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder="Alex Chen"
placeholderTextColor="#94a3b8"
/>
<Text style={styles.preview}>
{name.length === 0 ? "Escribe para previsualizar" : `Hola, ${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 recibe una cadena - no un evento sintético como onChange webvalue={name} hace que el campo nativo sea un componente controlado; omite value solo para prototipos completamente no controladossetName("") desde un botón o después de envío exitosoRelacionado: react-hook-form en Mobile - menos re-renderizados cuando muchas entradas comparten una pantalla
keyboardType selecciona el diseño del teclado de software. Combínalo con autoCapitalize y autoCorrect para que los usuarios obtengan las teclas correctas y el comportamiento de autocorrección 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="you@example.com"
/>
</Field>
<Field label="Edad">
<TextInput
style={styles.input}
value={age}
onChangeText={setAge}
keyboardType="number-pad"
placeholder="25"
/>
</Field>
<Field label="Teléfono">
<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 y url incluyen @ y / - aún así establece autoCapitalize="none" porque los defaults de mayúsculas de oración son molestos en esos camposnumber-pad y phone-pad no tienen tecla Return - planifica envío mediante un botón en pantalla o mueve el enfoque con refstextContentType (iOS) y autoComplete (Android) mejoran el completado automático del gestor de contraseñas y SMS - expande en el ejemplo de contraseña a continuaciónRelacionado: Selectores, Fecha/Hora e Entradas Nativas - cuando un selector nativo supera entrada de texto libre
returnKeyType re-etiqueta la tecla de acción del teclado - "Next", "Done", "Go", "Search". Empareja la etiqueta con lo que realmente sucede cuando el usuario la 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}>Buscar</Text>
<TextInput
style={styles.input}
value={query}
onChangeText={setQuery}
returnKeyType="search"
onSubmitEditing={() => console.log("search:", query)}
placeholder="Buscar artículos"
/>
<Text style={styles.label}>Biografía</Text>
<TextInput
style={[styles.input, styles.multiline]}
value={note}
onChangeText={setNote}
multiline
returnKeyType="default"
blurOnSubmit={false}
placeholder="Una biografía corta - Return inserta una nueva línea"
/>
</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 en un asistente, done en el último campo de una sola línea, go cuando envías a un servidor, send en interfaces de chatblurOnSubmit={false} para que la tecla de acción no descarte el teclado inesperadamenteonSubmitEditing se activa cuando el usuario toca la tecla de acción - conéctalo para enfocar el siguiente campo o ejecutar lógica de envíoreturnKeyType de manera inconsistente en algunos teclados OEM - siempre proporciona un botón alternativo en pantallaRelacionado: Flujos de Asistente Multi-Paso -
returnKeyType="next"a través de límites de pasos
blurOnSubmit controla si tocar la tecla de acción descarta el teclado después de onSubmitEditing. Para formularios de varios campos, establece blurOnSubmit={false} en cada campo excepto el último para que el enfoque pueda moverse sin que el teclado se cierre.
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}>Ciudad</Text>
<TextInput
style={styles.input}
value={city}
onChangeText={setCity}
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => zipRef.current?.focus()}
placeholder="Portland"
/>
<Text style={styles.label}>Código postal</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 entradas de una sola línea - bien para una barra de búsqueda de un campo, incorrecto para cadenas "Next"blurOnSubmit={false} + onSubmitEditing → nextRef.focus() es el patrón de dos campos estándarblurOnSubmit en default (true) o establécelo explícitamente para que Done descarte el tecladoblurOnSubmit importa menos allí; confía en un botón Submit visibleRelacionado: Controlador de teclado - mantén entradas visibles cuando el teclado está abierto
Adjunta una ref a TextInput para llamar a .focus(), .blur() o .clear() imperativamente. Úsalo para autofocus al montar, saltar al primer campo inválido o atajos 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}>Ingresa código de verificación</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}>Enfocar entrada</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) - llama a ref.current?.focus() con encadenamiento opcional porque el nodo puede no estar montado aúnsetTimeout ~300ms) después de transiciones de navegación para que el teclado no compita con animaciones de pantallafirstErrorRef.current?.focus() lleva lectores de pantalla y usuarios videntes al campo problemáticoref={inputRef} en TextInput es el patrón idiomáticoRelacionado: Accesibilidad en formularios - anunciar errores y gestionar enfoque de VoiceOver/TalkBack
Encadena campos combinando returnKeyType="next", blurOnSubmit={false}, refs y onSubmitEditing. La tecla Next del teclado debe aterrizar en el siguiente campo lógico cada vez.
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}>Nombre</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}>Apellido</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 ayudan al SO a insertar valores de completado automático sin romper tu cadena de enfoquereturnKeyType="done" y llama tu gestor de envío en onSubmitEditingScrollView - desplaza el campo enfocado a la vista antes de llamar a .focus() en campos inferiores (consulta ejemplo 9)Relacionado: Mejores prácticas de formularios - reducir toques y prevenir pérdida de datos en navegación hacia atrás
Conecta un botón Enviar en pantalla para validar estado, llamar tu API y descartar el teclado. Usa keyboardShouldPersistTaps en scrollables padre para que el botón responda en el primer 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", "Ingresa una dirección de email válida.");
return;
}
setSubmitting(true);
Keyboard.dismiss();
try {
await fakeSubscribe(email);
Alert.alert("Suscrito", `Confirmación enviada a ${email}`);
setEmail("");
} finally {
setSubmitting(false);
}
}
return (
<ScrollView
contentContainerStyle={styles.screen}
keyboardShouldPersistTaps="handled"
>
<Text style={styles.heading}>Boletín</Text>
<TextInput
style={styles.input}
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
returnKeyType="go"
onSubmitEditing={handleSubmit}
placeholder="you@example.com"
/>
<Pressable
style={[styles.button, submitting && styles.buttonDisabled]}
onPress={handleSubmit}
disabled={submitting}
>
<Text style={styles.buttonText}>{submitting ? "Enviando..." : "Suscribirse"}</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() al inicio del envío para que el teclado no cubra la interfaz de éxitokeyboardShouldPersistTaps="handled" permite que Pressable reciba toques sin requerir un segundo toque para descartar el teclado primerosubmitting - previene posts dobles en redes lentasincludes("@") con Zod o tu esquema API antes de lanzar - consulta guía de validaciónRelacionado: Validación con Zod - errores orientados por esquema mostrados al lado de cada campo
Los campos de contraseña necesitan secureTextEntry, textContentType / autoComplete sensibles y a menudo un alternador de visibilidad - los usuarios no pueden revisar texto enmascarado.
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}>Contraseña</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 contraseña" : "Mostrar contraseña"}
>
<Text style={styles.toggleText}>{visible ? "Ocultar" : "Mostrar"}</Text>
</Pressable>
</View>
<Text style={styles.hint}>Mínimo 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 enmascara caracteres en pantalla y en algunas capturas - aún así trata el valor como sensible en logstextContentType="password" y autoComplete="password" - usa newPassword en flujos de registrosecureTextEntry - el valor de estado permanece igualRelacionado: Accesibilidad en formularios - etiquetado de campos seguros para tecnología asistiva
Los formularios de pantalla completa necesitan un contenedor de desplazamiento, evitación de teclado y manejo de toques para que campos cerca del fondo permanezcan alcanzables y botones permanezcan tocables.
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}>Solicitud de soporte</Text>
<View style={styles.field}>
<Text style={styles.label}>Asunto</Text>
<TextInput
style={styles.input}
value={subject}
onChangeText={setSubject}
returnKeyType="next"
blurOnSubmit={false}
placeholder="Resumen breve"
/>
</View>
<View style={styles.field}>
<Text style={styles.label}>ID de orden</Text>
<TextInput
style={styles.input}
value={orderId}
onChangeText={setOrderId}
returnKeyType="next"
blurOnSubmit={false}
placeholder="ORD-12345"
/>
</View>
<View style={styles.field}>
<Text style={styles.label}>Detalles</Text>
<TextInput
style={[styles.input, styles.multiline]}
value={details}
onChangeText={setDetails}
multiline
blurOnSubmit={false}
placeholder="Qué sucedió?"
/>
</View>
<Pressable style={styles.button} onPress={() => {}}>
<Text style={styles.buttonText}>Enviar solicitud</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 con behavior="padding" en iOS eleva el contenido cuando se abre el teclado - sintoniza keyboardVerticalOffset para la altura de tu encabezadokeyboardDismissMode="on-drag" permite a los usuarios arrastrar la vista de desplazamiento para descartar el teclado - esperado en formularios largoskeyboardShouldPersistTaps="handled" es requerido cuando botones de envío se encuentran dentro del mismo ScrollView que entradasreact-native-keyboard-controller - enlazado a continuaciónRelacionado: Controlador de teclado -
KeyboardAwareScrollViewy desplazamientos consistentes entre plataformas
Este ejemplo combina entradas controladas, tipos de teclado, una cadena de enfoque, enmascaramiento de contraseña, validación y envío - el patrón de línea base que la mayoría de aplicaciones extienden.
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("Verifica email", "Ingresa una dirección de email válida.");
return;
}
if (password.length < 8) {
Alert.alert("Verifica contraseña", "La contraseña debe tener al menos 8 caracteres.");
passwordRef.current?.focus();
return;
}
setSubmitting(true);
Keyboard.dismiss();
try {
await fakeSignIn(email, password);
Alert.alert("Iniciada sesión", `Bienvenido de vuelta, ${email}`);
} catch {
Alert.alert("Error de inicio de sesión", "Verifica tus credenciales e intenta de nuevo.");
} finally {
setSubmitting(false);
}
}
return (
<KeyboardAvoidingView
style={styles.flex}
behavior={Platform.OS === "ios" ? "padding" : undefined}
>
<ScrollView
contentContainerStyle={styles.content}
keyboardShouldPersistTaps="handled"
>
<Text style={styles.heading}>Iniciar sesión</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="you@example.com"
/>
<Text style={styles.label}>Contraseña</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 ? "Iniciando sesión..." : "Iniciar sesión"}</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 el usuario aterrice en el campo que necesita atenciónreturnKeyType="next" en email y go en contraseña reflejan la acción primaria en pantallaKeyboard.dismiss() antes del trabajo asincrónico evita que el teclado reaparezca bajo overlays de cargaAlert con texto de error inline bajo cada campo - Zod + react-hook-form hacen esto escalable en formularios más grandesRelacionado: react-hook-form en Mobile - misma UX con menos estado manual | Validación con Zod - esquemas reutilizables para reglas de email y contraseña
Versiones de stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0 y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 16 jul 2026