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.
  • systemImage is an SF Symbol name, such as bell.fill. Knighn cannot draw SF Symbols, so it draws a coloured tile (tint) and the export keeps the name.
  • SwiftSlider has level, where the knob sits on the track: '0', '50' or '100'. Pick the one nearest to value between min and max.
  • A segmented picker takes one SwiftOption for each option; mark one selected. A menu picker shows selection.
  • glass and glassProminent are the iOS 26 Liquid Glass buttons. Put them in a SwiftHStack under the form, not in a section.
  • Your own components can wrap these (for example a Row that renders a SwiftToggle): 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 in Stack.Screen.
  • components/<Name>.ios.tsx: the SwiftUI tree in Host, with useState for 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 }