A set of components that allow you to build UIs directly with SwiftUI and Jetpack Compose from React.
This library is currently in alpha and will frequently experience breaking changes. It is not available in the Expo Go app – use development builds to try it out.
@expo/ui
is a set of native input components that allows you to build fully native interfaces with SwiftUI and Jetpack Compose. It aims to provide the commonly used features and components that a typical app will need.
Learn about the basics of @expo/ui/swift-ui
Installation
-
npx expo install @expo/ui
If you are installing this in an existing React Native app, make sure to install expo
in your project.
Swift UI examples
BottomSheet
import { BottomSheet, Host, Text } from '@expo/ui/swift-ui'; import { useWindowDimensions } from 'react-native'; const { width } = useWindowDimensions(); <Host style={{ position: 'absolute', width }}> <BottomSheet isOpened={isOpened} onIsOpenedChange={e => setIsOpened(e)}> <Text>Hello, world!</Text> </BottomSheet> </Host>
See also: official SwiftUI documentation
Button
The borderless variant is not available on Apple TV.
import { Button, Host } from '@expo/ui/swift-ui'; <Host style={{ flex: 1 }}> <Button variant="default" onPress={() => { setEditingProfile(true); }}> Edit profile </Button> </Host>
See also: official SwiftUI documentation
CircularProgress
import { CircularProgress, Host } from '@expo/ui/swift-ui'; <Host style={{ width: 300 }}> <CircularProgress progress={0.5} color="blue" /> </Host>
See also: official SwiftUI documentation
ColorPicker
This component is not available on Apple TV.
import { ColorPicker, Host } from '@expo/ui/swift-ui'; <Host style={{ width: 400, height: 200 }}> <ColorPicker label="Select a color" selection={color} onValueChanged={setColor} /> </Host>
See also: official SwiftUI documentation
ContextMenu
Note: Also known as DropdownMenu.
import { ContextMenu, Host } from '@expo/ui/swift-ui'; <Host style={{ width: 150, height: 50 }}> <ContextMenu> <ContextMenu.Items> <Button systemImage="person.crop.circle.badge.xmark" onPress={() => console.log('Pressed1')}> Hello </Button> <Button variant="bordered" systemImage="heart" onPress={() => console.log('Pressed2')}> Love it </Button> <Picker label="Doggos" options={['very', 'veery', 'veeery', 'much']} variant="menu" selectedIndex={selectedIndex} onOptionSelected={({ nativeEvent: { index } }) => setSelectedIndex(index)} /> </ContextMenu.Items> <ContextMenu.Trigger> <Button variant="bordered"> Show Menu </Button> </ContextMenu.Trigger> </ContextMenu> </Host>
See also: official SwiftUI documentation
DateTimePicker (date)
This component is not available on Apple TV.
import { DateTimePicker, Host } from '@expo/ui/swift-ui'; <Host matchContents> <DateTimePicker onDateSelected={date => { setSelectedDate(date); }} displayedComponents='date' initialDate={selectedDate.toISOString()} variant='wheel' /> </Host>
See also: official SwiftUI documentation
DateTimePicker (time)
This component is not available on Apple TV.
import { DateTimePicker, Host } from '@expo/ui/swift-ui'; <Host matchContents> <DateTimePicker onDateSelected={date => { setSelectedDate(date); }} displayedComponents='hourAndMinute' initialDate={selectedDate.toISOString()} variant='wheel' /> </Host>
See also: official SwiftUI documentation
Gauge
This component is not available on Apple TV.
import { Gauge, Host } from "@expo/ui/swift-ui"; <Host matchContents> <Gauge max={{ value: 1, label: '1' }} min={{ value: 0, label: '0' }} current={{ value: 0.5 }} color={[ PlatformColor('systemRed'), PlatformColor('systemOrange'), PlatformColor('systemYellow'), PlatformColor('systemGreen'), ]} type="circularCapacity" /> </Host>
See also: official SwiftUI documentation
Host
A component that allows you to put the other @expo/ui/swift-ui
components in React Native. It acts like <svg>
for DOM, <Canvas>
for react-native-skia
, which underlying uses UIHostingController
to render the SwiftUI views in UIKit.
Since the Host
component is a React Native View
, you can pass the style
prop to it or matchContents
prop to make the Host
component match the contents' size.
import { Button, Host } from '@expo/ui/swift-ui'; function Example() { return ( <Host matchContents> <Button onPress={() => { console.log('Pressed'); }}> Click </Button> </Host> ); }
import { Button, Host, VStack, Text } from '@expo/ui/swift-ui'; function Example() { return ( <Host style={{ flex: 1 }}> <VStack spacing={8}> <Text>Hello, world!</Text> <Button onPress={() => { console.log('Pressed'); }}> Click </Button> </VStack> </Host> ); }
LinearProgress
import { LinearProgress, Host } from '@expo/ui/swift-ui'; <Host style={{ width: 300 }}> <LinearProgress progress={0.5} color="red" /> </Host>
See also: official SwiftUI documentation
List
import { Host, List } from '@expo/ui/swift-ui'; <Host style={{ flex: 1 }}> <List scrollEnabled={false} editModeEnabled={editModeEnabled} onSelectionChange={(items) => alert(`indexes of selected items: ${items.join(', ')}`)} moveEnabled={moveEnabled} onMoveItem={(from, to) => alert(`moved item at index ${from} to index ${to}`)} onDeleteItem={(item) => alert(`deleted item at index: ${item}`)} listStyle='automatic' deleteEnabled={deleteEnabled} selectEnabled={selectEnabled}> {data.map((item, index) => ( <LabelPrimitive key={index} title={item.text} systemImage={item.systemImage} color={color} /> ))} </List> </Host>
See also: official SwiftUI documentation
Picker (segmented)
import { Host, Picker } from '@expo/ui/swift-ui'; <Host matchContents> <Picker options={['$', '$$', '$$$', '$$$$']} selectedIndex={selectedIndex} onOptionSelected={({ nativeEvent: { index } }) => { setSelectedIndex(index); }} variant="segmented" /> </Host>
See also: official SwiftUI documentation
Picker (wheel)
The wheel variant is not available on Apple TV.
import { Host, Picker } from '@expo/ui/swift-ui'; <Host style={{ height: 100 }}> <Picker options={['$', '$$', '$$$', '$$$$']} selectedIndex={selectedIndex} onOptionSelected={({ nativeEvent: { index } }) => { setSelectedIndex(index); }} variant="wheel" /> </Host>
See also: official SwiftUI documentation
Slider
This component is not available on Apple TV.
import { Host, Slider } from '@expo/ui/swift-ui'; <Host style={{ minHeight: 60 }}> <Slider value={value} onValueChange={(value) => { setValue(value); }} /> </Host>
See also: official SwiftUI documentation
Switch (toggle)
Note: Also known as Toggle.
import { Host, Switch } from '@expo/ui/swift-ui'; <Host matchContents> <Switch checked={checked} onValueChange={checked => { setChecked(checked); }} color="#ff0000" label="Play music" variant="switch" /> </Host>
See also: official SwiftUI documentation
Switch (checkbox)
import { Host, Switch } from '@expo/ui/swift-ui'; <Host matchContents> <Switch checked={checked} onValueChange={checked => { setChecked(checked); }} label="Play music" variant="checkbox" /> </Host>
See also: official SwiftUI documentation
TextField
import { Host, TextField } from '@expo/ui/swift-ui'; <Host matchContents> <TextField autocorrection={false} defaultValue="A single line text input" onChangeText={setValue} /> </Host>
See also: official SwiftUI documentation
More
Expo UI is still in active development. We continue to add more functionality and may change the API. Some examples in the docs may not be up to date. If you want to see the latest changes, check the examples.
Jetpack Compose examples
Button
import { Button } from '@expo/ui/jetpack-compose'; <Button style={{ flex: 1 }} variant="default" onPress={() => { setEditingProfile(true); }}> Edit profile </Button>
See also: official Jetpack Compose documentation
CircularProgress
import { CircularProgress } from '@expo/ui/jetpack-compose'; <CircularProgress progress={0.5} style={{ width: 300 }} color="blue" elementColors={{ trackColor: '#cccccc' }} />
See also: official Jetpack Compose documentation
ContextMenu
Note: Also known as DropdownMenu.
import { ContextMenu } from '@expo/ui/jetpack-compose'; <ContextMenu style={{ width: 150, height: 50 }}> <ContextMenu.Items> <Button elementColors={{ containerColor: '#0000ff', contentColor: '#00ff00' }} onPress={() => console.log('Pressed1')}> Hello </Button> <Button variant="bordered" color="#ff0000" onPress={() => console.log('Pressed2')}> Love it </Button> <Picker label="Doggos" options={['very', 'veery', 'veeery', 'much']} variant="menu" selectedIndex={selectedIndex} onOptionSelected={({ nativeEvent: { index } }) => setSelectedIndex(index)} /> </ContextMenu.Items> <ContextMenu.Trigger> <Button variant="bordered" style={{ width: 150, height: 50 }}> Show Menu </Button> </ContextMenu.Trigger> </ContextMenu>
See also: official Jetpack Compose documentation
DateTimePicker (date)
import { DateTimePicker } from '@expo/ui/jetpack-compose'; <DateTimePicker onDateSelected={date => { setSelectedDate(date); }} displayedComponents='date' initialDate={selectedDate.toISOString()} variant='picker' />
See also: official Jetpack Compose documentation
DateTimePicker (time)
import { DateTimePicker } from '@expo/ui/jetpack-compose'; <DateTimePicker onDateSelected={date => { setSelectedDate(date); }} displayedComponents='hourAndMinute' initialDate={selectedDate.toISOString()} variant='picker' />
See also: official Jetpack Compose documentation
LinearProgress
import { LinearProgress } from '@expo/ui/jetpack-compose'; <LinearProgress progress={0.5} style={{ width: 300 }} color="red" />
See also: official Jetpack Compose documentation
Picker (radio)
import { Picker } from '@expo/ui/jetpack-compose'; <Picker options={['$', '$$', '$$$', '$$$$']} selectedIndex={selectedIndex} onOptionSelected={({ nativeEvent: { index } }) => { setSelectedIndex(index); }} variant="radio" />
See also: official Jetpack Compose documentation
Picker (segmented)
import { Picker } from '@expo/ui/jetpack-compose'; <Picker options={['$', '$$', '$$$', '$$$$']} selectedIndex={selectedIndex} onOptionSelected={({ nativeEvent: { index } }) => { setSelectedIndex(index); }} variant="segmented" />
See also: official Jetpack Compose documentation
Slider
import { Slider } from '@expo/ui/jetpack-compose'; <Slider style={{ minHeight: 60 }} value={value} onValueChange={(value) => { setValue(value); }} />
See also: official Jetpack Compose documentation
Switch (toggle)
Note: also known as Toggle.
import { Switch } from '@expo/ui/jetpack-compose'; <Switch value={checked} onValueChange={checked => { setChecked(checked); }} color="#ff0000" label="Play music" variant="switch" />
See also: official Jetpack Compose documentation
Switch (checkbox)
import { Switch } from '@expo/ui/jetpack-compose'; <Switch value={checked} onValueChange={checked => { setChecked(checked); }} label="Play music" color="#ff0000" variant="checkbox" />
See also: official Jetpack Compose documentation
TextInput
import { TextInput } from '@expo/ui/jetpack-compose'; <TextInput autocorrection={false} defaultValue="A single line text input" onChangeText={setValue} />
See also: official Jetpack Compose documentation
API
Full documentation is not yet available. Use TypeScript types to explore the API.
// Import from the SwiftUI package import { BottomSheet } from '@expo/ui/swift-ui';
// Import from the Jetpack Compose package import { Button } from '@expo/ui/jetpack-compose';