Reference version

useNativeState

A React hook that creates observable state shared between JavaScript and native SwiftUI views.

iOS
tvOS
Included in Expo Go
Bundled version:
~56.0.2

For the complete documentation index, see llms.txt. Use this file to discover all available pages.

useNativeState returns an ObservableState that maps to a SwiftUI ObservableObject on the native side, so reads and writes to .value are observed directly by SwiftUI without going through the React render cycle. This lets you update the native view synchronously from a worklet on the UI thread.

Installation

Terminal
- npx expo install @expo/ui

If you are installing this in an existing React Native app, make sure to install expo in your project.

Usage

Note: Using worklets requires installing react-native-reanimated and react-native-worklets in your project. useNativeState itself works without them, but the synchronous UI-thread updates shown below depend on the worklet runtime.

The example below masks a phone number as the user types. The formatting and the write to maskedPhone.value both happen synchronously on the UI thread, so there is no flicker between the typed value and the masked value.

WorkletPhoneMaskExample.tsx
import { Host, TextField, TextFieldRef, useNativeState } from '@expo/ui/swift-ui'; import { keyboardType } from '@expo/ui/swift-ui/modifiers'; import { useCallback, useRef } from 'react'; import { runOnJS } from 'react-native-worklets'; export default function WorkletPhoneMaskExample() { const phoneRef = useRef<TextFieldRef>(null); const maskedPhone = useNativeState(''); const setPhoneCursor = useCallback((position: number) => { phoneRef.current?.setSelection(position, position); }, []); return ( <Host matchContents> <TextField ref={phoneRef} text={maskedPhone} placeholder="(555) 123-4567" modifiers={[keyboardType('phone-pad')]} onTextChange={v => { 'worklet'; const digits = v.replace(/\D/g, '').slice(0, 10); let formatted: string; if (digits.length === 0) { formatted = ''; } else if (digits.length <= 3) { formatted = digits; } else if (digits.length <= 6) { formatted = `(${digits.slice(0, 3)}) ${digits.slice(3)}`; } else { formatted = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`; } if (formatted !== v) { maskedPhone.value = formatted; runOnJS(setPhoneCursor)(formatted.length); } }} /> </Host> ); }

API

import { useNativeState } from '@expo/ui/swift-ui';

Hooks

useNativeState(initialValue)

iOS
tvOS
ParameterType
initialValueT

Creates an observable native state that is automatically cleaned up when the component unmounts.

Returns:
ObservableState<T>

Types

ObservableState

iOS
tvOS

Observable state shared between JavaScript and native views (Jetpack Compose on Android and SwiftUI on iOS).

Type: SharedObject extended by:

PropertyTypeDescription
valueT

The current value. Reads are safe from any thread; prefer writing from a worklet so the update runs on the native UI thread. Updating state from the JS thread might show a development warning.