Guide: Expo SwiftUI screens
Use this guide when the user builds an iOS app with Expo and `@expo/ui`, or asks for native SwiftUI controls. Screens made this way export as Expo code that use
Use this guide when the user builds an iOS app with Expo and @expo/ui, or asks for native SwiftUI controls. Screens made this way export as Expo code that uses the real controls.
Start from the template
Put in the expo-swiftui template. It adds the components, the iOS system colour tokens (light and dark) and four screens.
const { screens } = InsertTemplate('expo-swiftui', { page })
Print(Tree(screens[0], { depth: 3 }))
The components
Each Swift<Name> component draws the iOS 26 look of the @expo/ui/swift-ui component <Name>, and its props have the same names. Build screens only from these components, so that the export can map every part.
| Component | @expo/ui | Props |
|---|---|---|
IOSScreen |
Host |
title, children. The root of each screen: large title at the top, home indicator. 402 by 874. |
SwiftForm |
Form |
children: sections. |
SwiftSection |
Section |
title, footer, children: rows. |
SwiftToggle |
Toggle |
label, isOn, systemImage, tint (blue, green or red). |
SwiftPicker |
Picker |
label, selection, pickerStyle (menu or segmented), children. |
SwiftOption |
Text with tag |
label, selected. The options of a segmented picker. |
SwiftSlider |
Slider |
label, value, min, max, level. |
SwiftStepper |
Stepper |
label, value, min, max. |
SwiftTextField |
TextField |
placeholder, text. |
SwiftLabeledContent |
LabeledContent |
label, value. |
SwiftLabel |
Label |
title, systemImage, tint. A navigation row. |
SwiftButton |
Button |
label, role (default or destructive), buttonStyle (automatic, borderedProminent, glass, glassProminent), systemImage. |
SwiftText |
Text |
text, textStyle (headline, body or footnote). |
SwiftSheet |
BottomSheet |
children. A sheet over the screen. |
SwiftHStack |
HStack |
children. |
systemImageis an SF Symbol name, such asbell.fill. Knighn cannot draw SF Symbols, so it draws a coloured tile (tint) and the export keeps the name.SwiftSliderhaslevel, where the knob sits on the track:'0','50'or'100'. Pick the one nearest tovaluebetweenminandmax.- A segmented picker takes one
SwiftOptionfor each option; mark oneselected. A menu picker showsselection. glassandglassProminentare the iOS 26 Liquid Glass buttons. Put them in aSwiftHStackunder the form, not in a section.- Your own components can wrap these (for example a
Rowthat renders aSwiftToggle): the export follows them.
<IOSScreen title="Settings">
<SwiftForm>
<SwiftSection title="Reminders" footer="Once a day.">
<SwiftToggle label="Daily reminder" isOn systemImage="bell.fill" tint="red" />
<SwiftPicker label="Time" selection="8:00 am" />
</SwiftSection>
</SwiftForm>
</IOSScreen>
Export
Export(screen, 'expo') returns { files, skipped }. The files are the layout that @expo/ui needs, because SwiftUI runs on iOS only and Expo Router refuses platform suffixes in app/:
app/<route>.tsx: the route, with the title inStack.Screen.components/<Name>.ios.tsx: the SwiftUI tree inHost, withuseStatefor each control that the person changes.components/<Name>.tsx: the fallback for Android and the web.
skipped names the parts with no @expo/ui match, such as an <img>. Replace them with template components, or tell the user.
const { files, skipped } = await Export('Settings', 'expo')
return { paths: files.map((file) => file.path), skipped }