Navigation decision report · as of 31 October 2026
Expo UI vs React Navigation for Expo app navigation
What does "use Expo UI for navigation" mean today, how does it compare with React Navigation and Expo Router, and which should you choose? Every material claim links to a primary source (Expo or React Navigation docs, changelogs, source code and issues).
TL;DR
- Expo UI is not a navigation system. SDK 58 added SwiftUI
NavigationStack,NavigationSplitView,TabViewandToolbarto@expo/ui. They run on iOS and tvOS only, live inside one<Host>, and are driven by component state, with no URLs, deep links or routes.5,6 Android (Compose) has only screen chrome such as a bottomNavigationBarandTopAppBar, with no stack.7 - The real choice for a new Expo app is Expo Router vs React Navigation 7. Since SDK 56, Expo Router runs on its own fork of React Navigation's core, not on
@react-navigation/*.8 Its stable native tabs and toolbars (SDK 58) are where Expo's native, Liquid Glass navigation lives.1,9 - Recommendation: use Expo Router (native
Stack+NativeTabs) as the app shell. Use Expo UI inside screens for native controls, and itsNavigationStack/NavigationSplitViewonly for self-contained iOS sub-flows. Stay on React Navigation 7 if you have an existing app on it, need dynamic navigator trees, or rely on features Expo Router doesn't support.
@react-navigation/* inside an Expo Router app. A codemod rewrites those imports to expo-router/react-navigation and related paths.8,10 expo-router@58's npm dependencies no longer include the React Navigation packages.2 Expo says its "focus in the navigation space has shifted towards Expo Router", and that React Navigation still works in Expo projects.11The three things being compared
Expo UI navigation containers
NavigationStack · NavigationSplitView · TabView · Toolbar
inside one <Host> (SwiftUI)
iOS / tvOS only · state-driven
Component-level UI with real SwiftUI containers. It doesn't know about routes.
Expo Router 58
Stack · NativeTabs stable · Stack.Toolbar
forked RN core → react-native-screens
Expo's app-level navigation. iOS, Android and web.
React Navigation 7.x
@react-navigation/* → react-native-screens 4.x
Library-level navigation, configured in code. iOS, Android and web.
What "Expo UI for navigation" concretely is today
@expo/ui renders real SwiftUI and Jetpack Compose components from React. Each tree sits inside a Host.12 The SwiftUI and Compose APIs have been stable since SDK 56. The cross-platform "Universal" components run on web as experimental.13,11
| Navigation piece | In @expo/ui 58? | Notes |
|---|---|---|
NavigationStack / NavigationLink / NavigationDestination | Yes, iOS new in 58.0.0 | Controlled by path/onPathChange. Content presented from the stack (sheet, popover) needs its own stack.5,14 |
NavigationSplitView (2–3 columns) | Yes, iOS new in 58.0.3 | Collapses to one column on narrow windows. iOS 16+.6,14 |
TabView (incl. sidebarAdaptable) | Yes, iOS | The docs say: "For routed bottom-tab navigation across full-screen routes, use expo-router/native-tabs instead."15 |
Toolbar, navigationTitle, searchable | Yes, iOS | searchable arrived in 58.0.15.14,i1 |
| Sheets, menus, lists | Yes | BottomSheet with detents, Menu, ContextMenu, Popover, List, Picker (iOS); ModalBottomSheet (Android).12,7 |
| Android stack / NavHost / rail | No | Compose offers NavigationBar, TopAppBar, SearchBar and a floating toolbar, with no navigation container.7 |
| Routes, URLs, deep links | No | Nothing in the docs connects these containers to Expo Router or linking.5 |
Bottom line: Expo UI gives you native SwiftUI navigation containers for part of a screen on iOS, not an app-wide, cross-platform, URL-addressable navigation system. No Expo source presents it as a replacement for Expo Router or React Navigation. The Expo UI stable announcement positions it for "settings, modals, pickers, sheets" that should look like the OS.13
Side-by-side comparison
| Dimension | Expo UI containers | Expo Router 58 (native) | React Navigation 7.x |
|---|---|---|---|
| Maturity & stability | Library stable since SDK 56, but the navigation containers only landed in Sept 2026 (58.0.x).14 | Native tabs and Stack.Toolbar stable in SDK 58. SplitView, ExperimentalStack and zoom transitions are alpha.9,16,17 |
7.0 shipped Nov 2024, now 7.5.1. Native bottom tabs are unstable. 8.0 is alpha.51 with no beta date.3,18,19 |
| Platform coverage | iOS/tvOS only. Android has chrome only; web has no navigation containers.5,7 | iOS, Android, web. Native tabs fall back to a basic layout on web, and Android allows at most 5 tabs.9 | iOS, Android, web. Web "works best with fully client-side rendered apps". Native tabs have no web support.20,18 |
| iPad / large screens | Real NavigationSplitView and sidebarAdaptable TabView. These are the strongest iPad primitives, but iOS-only and unrouted.6,15 |
Tab bar can move to the top, with a sidebar via sidebarAdaptable (iOS 18+). A routed split view exists only as alpha SplitView, root-only.21,16 |
Native tabs tabBarControllerMode: 'tabSidebar' (iOS 18+, unstable). drawerType: 'permanent' for wide screens.18,22 |
| Native look & feel | These are SwiftUI views, so Liquid Glass, toolbars, titles and search come from the OS itself.12 | Liquid Glass tab bar, minimize-on-scroll (iOS 26+), search tab (iOS 26.1+), large titles, Stack.SearchBar, form sheets, Link previews (iOS).9,23,24 |
Native stack uses UINavigationController/Fragment, with large titles, search bar (iOS 26 integrated) and Liquid Glass header options. Header items are unstable_.25 |
| Stack / tab / drawer / modal coverage | Stack, split and tabs on iOS. Sheets and popovers. No drawer.12 | Stack, NativeTabs, JS tabs, Drawer layout, modals and form sheets.10,26 |
Broadest: native and JS stack, tabs, native tabs (unstable), drawer, material top tabs, form sheets with detents.25,27 |
| Deep linking & typed routes | None. You map URLs to path state yourself.5 |
Automatic deep link for every file route. Typed routes beta, off by default.28,29 | Static API gives automatic types (StaticParamList) and linking={{enabled:'auto'}}. Dynamic API uses a manual linking config.30,31 |
| Gestures & transitions | Whatever SwiftUI's NavigationStack does natively (inference; not separately documented). |
Native stack transitions. Link.AppleZoom alpha, iOS 18+.17 |
Fixed native-stack animations. JS stack is fully customizable. Shared elements are experimental. No Android predictive back.27,32,33 |
| Performance | Native containers, but React Native content hosted inside SwiftUI has open bugs (blank in List, resize snapping).i2,i3 |
Native tabs render all tabs up front and have limited FlatList support.9 | Docs recommend native-stack over JS stack. Tabs are lazy by default; freezeOnBlur is opt-in.27,34 Neither project publishes head-to-head benchmarks. |
| Interop & migration | Drop into any screen of either system with <Host>. Nesting its NavigationStack inside a Router/RN stack screen is undocumented (double nav bars risk), so test it on a device. |
From RN: official guide maps navigators to _layout files. It doesn't support independent containers, initialState or non-serializable params.35 Pre-56 apps need the import codemod.10 |
Existing apps stay put. 8.0 will need React 19, RN 0.83, SDK 55+ and gesture-handler 3.19 |
| Ecosystem (npm, wk of 23–29 Oct 2026) | 6.76M/wk, inflated because expo-router 56–58 depends on it (inference).36 |
10.52M/wk.36 | 7.40M/wk for @react-navigation/native. 24.5k GitHub stars.36,37 |
Known gaps and open issues (sample)
| Area | Issue |
|---|---|
| Expo UI | expo#52692: React Native views in a SwiftUI List go blank · expo#52396: hosted RN content snaps during SwiftUI resize animations |
| Expo Router native tabs | expo#52654: changing role on a mounted tab crashes (iOS 26.1+) · expo#43660: selectedStyle ignored on iPad · expo#51794: Stack.Protected drops the tab · expo#51740: cross-tab form sheet stuck · expo#52397: redirect to group tab renders blank. Documented limits: no nested native tabs and no runtime add/remove of tabs.9 |
| React Navigation (iOS 26 / iPad) | #13067: headerTitle ignores headerRight · #12932: headerTintColor broken · #12908: native tab labels truncated · #13262: tab bar disappears after rotation · #12843: iPad scrolls to top on back |
| Shared (react-native-screens) | screens#5003: Liquid Glass header items jump instead of morphing · screens#4769: formSheet ScrollView sizing. Both Expo Router and React Navigation sit on this layer. |
Recommendation
Default: Expo Router shell + Expo UI inside screens
- App navigation in Expo Router SDK 58:
Stackfor pushes and sheets,NativeTabs(stable) for the Liquid Glass tab bar, iPad sidebar and search tab.9 - You get deep links for free, plus web and one codebase for iOS and Android.28
- Use
@expo/uifor native controls and, on iOS, for contained sub-flows (settings, a split-view browser inside one tab). This matches Expo's ownTabViewguidance.15 - Avoid alpha pieces (
SplitView,ExperimentalStack, zoom) in production paths.16
When the other choice wins
- React Navigation 7: you have an existing RN app, a brownfield app, dynamic or conditional navigator trees,
independentcontainers, non-serializable params, or material top tabs. It's also the choice when you need configuration in code rather than files.35,27 - Expo UI navigation containers as the main structure: only for an iOS/iPadOS-only app that wants real SwiftUI
NavigationSplitViewbehavior and accepts building URL handling and Android navigation itself. Expect to write custom native modules for anything Expo UI doesn't expose (e.g. tab sections). - Wait for React Navigation 8 only if you need its inferred types, native tabs by default and SSR, and can tolerate an alpha.19
Decision table
| Your situation | Choose |
|---|---|
| New Expo app, iOS + Android (+ web) | Expo Router 58 with NativeTabs and Stack; @expo/ui inside screens |
| Want Liquid Glass tab bar and iPad sidebar, production-ready today | Expo Router NativeTabs (stable) over RN 7 native tabs (unstable) |
| Existing React Navigation 7 app, working well | Stay on React Navigation 7; add @expo/ui components where native UI helps |
| Existing Expo Router ≤55 app | Upgrade along Expo Router; run the SDK 56 import codemod |
| iPad-only app needing a true multi-column split | Expo UI NavigationSplitView inside a Router screen (prototype first), or wait for Router SplitView to leave alpha |
| Android-first or Android parity required | Not Expo UI for navigation; use Expo Router or React Navigation |
| Deep links and typed URLs are core | Expo Router (automatic links) or RN 7 static API, never Expo UI alone |
Sources
- Expo SDK 58 changelog
- npm registry: expo-router 58.0.29 · SDK 58 bundledNativeModules.json
- React Navigation releases · React Navigation 7.0 blog
- react-native-screens 4.30.1
- Expo UI NavigationStack
- Expo UI NavigationSplitView
- Expo UI Jetpack Compose · Compose source (sdk-58)
- Expo Router v56: decoupling from React Navigation
- Expo Router native tabs guide
- Expo Router migration: SDK 55 → 56
- Expo SDK 56 changelog
- Expo UI overview · SwiftUI exports (sdk-58)
- Expo UI is stable (SDK 56)
- @expo/ui CHANGELOG (sdk-58)
- Expo UI TabView
- Expo Router SplitView (alpha) · ExperimentalStack (alpha)
- Expo Router zoom transition (alpha)
- React Navigation native bottom tabs
- React Navigation 8.0 alpha · 8.0 July progress
- React Navigation web support
- Expo Router NativeTabs API reference
- React Navigation drawer
- Expo Router Stack API · Stack.Toolbar · Modals & form sheets
- Expo Router Link preview
- React Navigation native stack
- Expo Router introduction
- React Navigation stack · material top tabs
- Expo Router static rendering · introduction
- Expo Router typed routes (beta)
- React Navigation static configuration · getting started
- React Navigation TypeScript · deep linking
- React Navigation shared element transitions
- React Navigation getting started (predictive back note)
- React Navigation bottom tabs
- Expo Router: migrate from React Navigation
- npm weekly downloads: expo-router · @react-navigation/native · @expo/ui
- react-navigation/react-navigation on GitHub
- expo#50346: searchable request, closed by 58.0.15