Available for projects Learn more

Mixing React Native with SwiftUI: A Buffet Approach

react-native swiftui mobile hybrid ios

The big promise of React Native is that you can ship an app to iOS, Android, and the web from one codebase. That’s incredibly powerful. But there’s a price: some screens don’t feel quite as native as their SwiftUI or Jetpack Compose counterparts. It’s just true.

By using the React Native building blocks and components, it is very easy to get a mobile app that is not a native app. As I’ve been building an Expo app that also runs on the web, this has further highlighted the issue. It’s very easy to get a working UI that feels usable but not native.

The idea I want to explore here is: what if we didn’t have to choose? What if, for the most important screens, we could drop down to SwiftUI (on iOS) or Kotlin (on Android) while still enjoying the productivity and reach of React Native/Expo for the rest of the app?

Why?

React Native is brilliant for most screens. It gives you the cross-platform win without reinventing the wheel. But for those final polish screens, the ones where user interaction is critical, it sometimes falls short. The lag is real, and sometimes it matters.

SwiftUI can be slotted into a React Native project because under the hood it’s just a UIHostingController.

The same idea applies to Kotlin screens on Android. This isn’t a replacement for React Native but a complement: a buffet-style approach where you pick the right tool for the right screen.

Settings screens, map views, media players—these benefit from the polish and responsiveness of native code.

Modals and alerts should be presented using system components not a custom view overlay.

A Settings screen based on a Form layout within a NavigationView feels very iOS. This feels native when presented as a modal using Expo Router.

How?

Here’s the beauty: I don’t need to know all the boilerplate.

Once the integration is set up for one SwiftUI screen, it’s easy to replicate.

From then on, I can open Xcode, preview the SwiftUI screen, and work as if I were building a fully native app—while still relying on Expo and EAS for the heavy lifting of deployment and deployment.

Yes, it adds more boilerplate to glue the layers together. But if you’ve ever shipped an app on iOS, Android, or the web, you’ll know that boilerplate and third party dependencies come with the territory.

But this isn’t even about the tooling, it’s not really about Xcode being a better environment for writing SwiftUI code. You can still ask AI to create the code you need, drop it into your project in Cursor, and within a short cycle be working with your screen in an iOS simulator.

Concepts

  • Module: logical groups of generic classes, likely to be YourAppUIModule, with individual screens.
  • Props, per screen:
    • ObservableObject with variables and callback functions.
  • UIModule: contains ExpoView and UIHostingController with view wrapper.
  • SwiftUI View: actual SwiftUI view file you edit in Xcode with Previews.

The whole approach is treated as a package and deployed with Cococapods (like the rest of React Native). There is also a ton of Expo and Xcode configuration too.

RN/Expo screens Typescript files contain a platform switch to show SwiftUI component if iOS.

When It Makes Sense

  • Settings: users expect these to feel like iOS screens. The default styling for an iOS native Settings screen is not easily repoduced using Expo.
  • Forms: its important that input screens are responsive. SwiftUI makes it easy to create these screens.
  • Screens with tightly bound logic: if a screen depends heavily on native APIs or foundational models, it might be easier to implement directly in SwiftUI.

It’s important to have realistic expectations about cross platform development.

When It Doesn’t

Not everything needs to be native. Generic menus, simple lists or grids don’t gain much from SwiftUI. For these, React Native is perfect—fast to build, easy to style, and fully cross-platform.

Simpler with AI

This hybrid approach is only possible because of years of hard work by others. The Expo team, the React Native ecosystem, Apple’s SwiftUI engineers—none of this is trivial. We’re standing on the shoulders of giants, standing on top of Javascript stacks.

This is the sort of approach that would be seen as impractical due to being overly complicated without AI. There are many config steps to remember in the setup and implementation. Setting up a new variable in a SwiftUI view requires you to remember to specify in the module and call it from the TypeScript file. AI can help with this. AI can help with all of this.

Routes I Did Not Take

There are React Native packages that try to mimic SwiftUI components. I deliberately didn’t go that way. For example, the alpha release of Expo UI. I want the full SwiftUI developer experience—open a Swift file in Xcode, use live previews, and work natively.

Conclusion

React Native doesn’t have to mean sacrificing a native feel. With a bit of setup, you can build a cross-platform app while still leaning on SwiftUI or Kotlin where it matters most. It’s a buffet approach: React Native for speed and reach, SwiftUI for polish.