This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
TextInput
A text input backed by native SwiftUI and Jetpack Compose components, with a React Native-compatible API.
Android
iOS
Web
Included in Expo Go
Recommended version:
~56.0.26
A text input that routes to TextField from @expo/ui/jetpack-compose on Android, TextField from @expo/ui/swift-ui on iOS, and React Native's TextInput on web.
The API mirrors React Native's TextInput, with two changes: value and selection are observable state objects (created with useNativeState), and onChangeText can be a worklet for synchronously updating the state on the UI thread.
Omit value and the field manages its own text internally. Use onChangeText to observe edits, and use the ref for imperative actions like focus, blur, and clear.
Pass value to drive the field from a useNativeState observable. The example below replaces Hello with World as you type.
ControlledTextInputExample.tsx
import{Host,TextInput, useNativeState }from'@expo/ui';import{ useCallback }from'react';exportdefaultfunctionControlledTextInputExample(){const text =useNativeState('');const handleChangeText =useCallback((value:string)=>{'worklet';
text.value= value ==='Hello'?'World': value;},[text]);return(<HostmatchContents={{ vertical:true}}><TextInputvalue={text}placeholder="Type here"onChangeText={handleChangeText}/></Host>);}
Worklet masking
Add the 'worklet' directive to onChangeText for synchronously updating the state on the UI thread. Writes to value land without the JS-thread round-trip that can cause cursor flicker.
import{Host,TextInput, useNativeState }from'@expo/ui';import{ useCallback }from'react';functionformatPhone(input:string){'worklet';const digits = input.replace(/\D/g,'').slice(0,10);if(digits.length<=3)return digits;if(digits.length<=6)return`(${digits.slice(0,3)}) ${digits.slice(3)}`;return`(${digits.slice(0,3)}) ${digits.slice(3,6)}-${digits.slice(6)}`;}exportdefaultfunctionPhoneMaskExample(){const phone =useNativeState('');const selection =useNativeState({ start:0, end:0});const handleChangeText =useCallback((value:string)=>{'worklet';const formatted =formatPhone(value);if(formatted !== value){
phone.value= formatted;// Snaps to end for demo. Real masks need smarter cursor handling.
selection.value={ start: formatted.length, end: formatted.length};}},[phone, selection]);return(<HostmatchContents={{ vertical:true}}><TextInputvalue={phone}selection={selection}keyboardType="phone-pad"placeholder="(555) 123-4567"onChangeText={handleChangeText}/></Host>);}
Unsupported React Native props
Some React Native TextInput props are not supported, because Compose's TextField or SwiftUI's TextField does not expose an equivalent, or because the prop is replaced by a different mechanism. See the API section below for the supported props. If a missing prop blocks your use case, open an issue so it can be prioritized.