ClaudeExpoMay 27, 2026, 1:30 PM

Worklet integration in Expo UI: synchronously controlling SwiftUI and Compose state

A condensed section focused on the key takeaways first.

Original Post

Quick Digest

Summary

A condensed section focused on the key takeaways first.

claudeen

Worklet integration in Expo UI: synchronously controlling SwiftUI and Compose state Summary

Key Points

  • Point 1: In SDK 56, Expo UI comes with first-class integration with UI runtime worklets.
  • Point 2: Thanks to react-native-worklets.
  • Point 3: What that means is that we can run event callbacks synchronously on UI thread and control UI state synchronously.

Summary

This is an English summary of "Worklet integration in Expo UI: synchronously controlling SwiftUI and Compose state" published on 2026-05-27.

Key Points

  • Point 1: In SDK 56, Expo UI comes with first-class integration with UI runtime worklets.
  • Point 2: Thanks to react-native-worklets.
  • Point 3: What that means is that we can run event callbacks synchronously on UI thread and control UI state synchronously.

Full Translation

Translations

A translation section that keeps the flow of the original article.

claudeja

Worklet integration in Expo UI: synchronously controlling SwiftUI and Compose state(原文タイトル)

概要

公開日: 2026-05-27 翻訳生成に失敗したため、原文をそのまま保存しています。

原文

In SDK 56, Expo UI comes with first-class integration with UI runtime worklets. Thanks to react-native-worklets. What that means is that we can run event callbacks synchronously on UI thread and control UI state synchronously. It means you can do this: import { Host, TextInput, useNativeState } from '@expo/ui'; export default function Screen() { const value = useNativeState(''); return ( <Host matchContents> <TextInput value={value} placeholder="Type something" onChangeText={(value) => { 'worklet'; // Runs synchronously on the UI thread, on every keystroke. console.log('[UI thread] typed:', value); }} /> </Host> ); } Note: you'll need react-native-reanimated and react-native-worklets installed in your project for this to work. What's actually happening Two pieces work together here: - useNativeState creates an ObservableState, a SharedObject that lives on native and is observed by both SwiftUI and Compose. Under the hood it maps to an ObservableObject on iOS and a MutableState on Android. - Worklet callbacks like onTextChange are executed directly on the UI thread when the native view fires its event. Together, this means each keystroke in the TextField updates the shared text state, runs your worklet, and lets SwiftUI and Compose re-render, all without ever hopping to the JS thread. If you've written SwiftUI, this should feel familiar. The TS code above maps almost 1:1 to: struct Screen: View { @State var text = "" var body: some View { TextField("Type something", text: $text) .onChange(of: text) { _, newValue in print("[UI thread] typed:", newValue) } } } useNativeState plays the role of @State, text={text} is the equivalent of TextField(text: $text), and the worklet onTextChange mirrors .onChange(of:). The same shape works on Compose with mutableStateOf and onValueChange. Flicker-free synchronous input masking The most immediate payoff of this is input masking that just works. Because the worklet can rewrite text.value on the same frame the keystroke arrives, the user never sees the unmasked character, there's no asynchronous round-trip through the JS thread. Here's a credit card field that formats 4242424242424242 into 4242 4242 4242 4242 as the user types, on the UI thread: import { Host, TextInput, useNativeState } from '@expo/ui/swift-ui'; export default function CardNumberField() { const value = useNativeState(''); return ( <Host matchContents> <TextInput value={value} placeholder="Card number" onChangeText={(value) => { 'worklet'; const digits = value.replace(/\D/g, '').slice(0, 16); const masked = digits.replace(/(.{4})/g, '$1 ').trim(); text.value = masked; }} /> </Host> ); } The same pattern works for phone numbers, dates, postal codes, currency, anything where the displayed text needs to differ from the raw keystrokes. Why worklet integration matters Worklet integration lets Expo UI deliver a more native-feeling UX, and gives you a synchronous alternative alongside the existing async one, so you can pick based on what the interaction needs. Input masking is just one of the usecases. The same native state + worklet pattern allows Expo UI to bring a lot more native state based SwiftUI and Compose APIs to React Native. We're excited to see where this goes next. Try it Worklet support works on both @expo/ui/swift-ui and @expo/ui/jetpack-compose, and is landed in SDK 56. TextInput is one of the first components wired up, expect more form controls to gain sync callbacks in upcoming releases.