The right keyboard is the cheapest validation. keyboardType accepts default, number-pad, decimal-pad, numeric, email-address, phone-pad and url on both platforms, plus visible-password on Android. Add autoCapitalize="none" and autoCorrect={false} for addresses and codes, and autoComplete (email, current-password, postal-code, one-time-code...) so autofill and password managers can help; iOS also reads textContentType.
Users can still paste anything, so validate too. The email below validates on blur (touched turns true when the field loses focus), so the error appears after the user leaves the field. The quantity is sanitized as the user types, so an invalid value never reaches state:
const bad = touched && !EMAIL.test(email); // EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
<TextInput style={ui.input} value={email} onChangeText={setEmail}
onBlur={() => setTouched(true)} keyboardType="email-address"
autoCapitalize="none" autoComplete="email" textContentType="emailAddress" />
{bad && <Text style={ui.error}>Enter an email like ana@example.com</Text>}
<TextInput style={ui.input} value={qty} keyboardType="number-pad" maxLength={1}
onChangeText={(t) => setQty(t.replace(/[^1-9]/g, ''))} />
<Text>Total: ${(14.99 * Number(qty || 0)).toFixed(2)}</Text>After "ana@example" and a tap on the quantity, the error appeared under the email, the number pad opened, and typing 3 showed "Total: $44.97". Show errors in text, not by color alone, next to the field; Text Accessibility makes them announce themselves to screen readers, and Forms and Validation replaces this hand-rolled state with React Hook Form 351,018 and Zod 44,027 .