Native Bottom Tabs in React Navigation: The Real Native Feel
React Navigation's createNativeBottomTabNavigator is the tab navigator you should be using in 2025. Unlike the classic JavaScript-based tab bar, this one uses UITabBarController on iOS and BottomNavigationView on Android — meaning gestures, animations, and haptics all feel exactly like a first-party app.
Why Native Tabs?
The JS-based tab navigator renders a custom view that tries to mimic the platform tab bar. It works, but it's always slightly off — the transitions feel different, it doesn't respond to iOS 18's sidebar mode, and you miss out on SF Symbols.
Native tabs fixes all of this. The trade-off: you need a development build (not Expo Go), and Android is limited to 5 tabs.
Requirements
- React Native 0.79+
- Expo SDK 53+
react-native-screens(latest)- A development build — this won't work in Expo Go
npm install @react-navigation/bottom-tabs react-native-screens
Basic Setup
Dynamic API (recommended):
import { createNativeBottomTabNavigator } from "@react-navigation/bottom-tabs"; const Tab = createNativeBottomTabNavigator(); export default function AppTabs() { return ( <Tab.Navigator> <Tab.Screen name="Home" component={HomeScreen} /> <Tab.Screen name="Explore" component={ExploreScreen} /> <Tab.Screen name="Profile" component={ProfileScreen} /> </Tab.Navigator> ); }
Static API:
const MyTabs = createNativeBottomTabNavigator({ screens: { Home: HomeScreen, Explore: ExploreScreen, Profile: ProfileScreen, }, });
Icons
SF Symbols (iOS)
The cleanest way on iOS — no icon library needed:
<Tab.Screen name="Home" component={HomeScreen} options={{ tabBarIcon: { sfSymbol: "house.fill" }, }} />
Image icons (cross-platform)
import { Image } from "react-native"; <Tab.Screen name="Home" component={HomeScreen} options={{ tabBarIcon: ({ color, size }) => ( <Image source={require("./assets/home.png")} style={{ width: size, height: size, tintColor: color }} /> ), }} />
System items (iOS only)
options={{ tabBarSystemItem: "bookmarks", // search, favorites, history, etc. }}
Styling
<Tab.Navigator screenOptions={{ tabBarActiveTintColor: "#000", tabBarInactiveTintColor: "#999", // Android only tabBarStyle: { backgroundColor: "#fff", }, }} >
iOS blur effect:
screenOptions={{ tabBarBlurEffect: "systemMaterial", // iOS 18 and below }}
Badges
options={{ tabBarBadge: 3, // number tabBarBadge: "New", // or string }}
iOS 18+ Features
Sidebar mode
On iPad and large iPhones, the tab bar can transform into a sidebar:
<Tab.Navigator screenOptions={{ tabBarControllerMode: "tabSidebar" }}>
Minimizable tabs (iOS 26+)
Tabs collapse when the user scrolls down, giving more screen space:
options={{ tabBarMinimizeBehavior: "onScrollDown", }}
Bottom accessory (iOS 26+)
Add a custom view below the tab bar — great for a floating action button:
<Tab.Navigator screenOptions={{ bottomAccessory: () => <MyFloatingButton />, }} >
Platform Differences
| Feature | iOS | Android |
|---|---|---|
| Max tabs | Unlimited | 5 |
| SF Symbols | ✅ | ❌ |
| Blur effect | ✅ (iOS 18-) | ❌ |
| Sidebar mode | ✅ (iOS 18+) | ❌ |
| Active indicator | ❌ | ✅ |
| Inactive tint | Via system | tabBarInactiveTintColor |
Events
<Tab.Screen name="Home" component={HomeScreen} listeners={{ tabPress: (e) => { // Scroll to top, refresh data, etc. console.log("Tab pressed"); }, }} />
Pro Tips
1. Use popToTopOnBlur to reset navigation when leaving a tab:
screenOptions={{ popToTopOnBlur: true }}
2. Lazy loading is on by default — screens only mount when first visited. Disable if you need a screen to be always mounted:
options={{ lazy: false }}
3. Programmatic navigation:
import { useNavigation } from "@react-navigation/native"; const navigation = useNavigation(); navigation.jumpTo("Profile");
Conclusion
If you're building a serious React Native app in 2025, createNativeBottomTabNavigator should be your default. The native components give you real platform behavior for free — iOS sidebar mode, Liquid Glass on iOS 26, SF Symbols, and correct haptics. The only real constraint is the 5-tab Android limit, which is rarely a problem in practice.
Switch to a development build, drop in the native navigator, and your tab bar will finally feel like it belongs on the platform.