Files
HausApp/.agents/skills/vercel-react-native-skills/rules/navigation-native-navigators.md
René Schober 4e34270786 initial commit
2026-03-13 06:23:06 +01:00

189 lines
4.8 KiB
Markdown

---
title: Use Native Navigators for Navigation
impact: HIGH
impactDescription: native performance, platform-appropriate UI
tags: navigation, react-navigation, expo-router, native-stack, tabs
---
## Use Native Navigators for Navigation
Always use native navigators instead of JS-based ones. Native navigators use
platform APIs (UINavigationController on iOS, Fragment on Android) for better
performance and native behavior.
**For stacks:** Use `@react-navigation/native-stack` or expo-router's default
stack (which uses native-stack). Avoid `@react-navigation/stack`.
**For tabs:** Use `react-native-bottom-tabs` (native) or expo-router's native
tabs. Avoid `@react-navigation/bottom-tabs` when native feel matters.
### Stack Navigation
**Incorrect (JS stack navigator):**
```tsx
import { createStackNavigator } from "@react-navigation/stack";
const Stack = createStackNavigator();
function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}
```
**Correct (native stack with react-navigation):**
```tsx
import { createNativeStackNavigator } from "@react-navigation/native-stack";
const Stack = createNativeStackNavigator();
function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}
```
**Correct (expo-router uses native stack by default):**
```tsx
// app/_layout.tsx
import { Stack } from "expo-router";
export default function Layout() {
return <Stack />;
}
```
### Tab Navigation
**Incorrect (JS bottom tabs):**
```tsx
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
const Tab = createBottomTabNavigator();
function App() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
```
**Correct (native bottom tabs with react-navigation):**
```tsx
import { createNativeBottomTabNavigator } from "@bottom-tabs/react-navigation";
const Tab = createNativeBottomTabNavigator();
function App() {
return (
<Tab.Navigator>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarIcon: () => ({ sfSymbol: "house" }),
}}
/>
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={{
tabBarIcon: () => ({ sfSymbol: "gear" }),
}}
/>
</Tab.Navigator>
);
}
```
**Correct (expo-router native tabs):**
```tsx
// app/(tabs)/_layout.tsx
import { NativeTabs } from "expo-router/unstable-native-tabs";
export default function TabLayout() {
return (
<NativeTabs>
<NativeTabs.Trigger name="index">
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
</NativeTabs.Trigger>
<NativeTabs.Trigger name="settings">
<NativeTabs.Trigger.Label>Settings</NativeTabs.Trigger.Label>
<NativeTabs.Trigger.Icon sf="gear" md="settings" />
</NativeTabs.Trigger>
</NativeTabs>
);
}
```
On iOS, native tabs automatically enable `contentInsetAdjustmentBehavior` on the
first `ScrollView` at the root of each tab screen, so content scrolls correctly
behind the translucent tab bar. If you need to disable this, use
`disableAutomaticContentInsets` on the trigger.
### Prefer Native Header Options Over Custom Components
**Incorrect (custom header component):**
```tsx
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{
header: () => <CustomHeader title="Profile" />,
}}
/>
```
**Correct (native header options):**
```tsx
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{
title: "Profile",
headerLargeTitleEnabled: true,
headerSearchBarOptions: {
placeholder: "Search",
},
}}
/>
```
Native headers support iOS large titles, search bars, blur effects, and proper
safe area handling automatically.
### Why Native Navigators
- **Performance**: Native transitions and gestures run on the UI thread
- **Platform behavior**: Automatic iOS large titles, Android material design
- **System integration**: Scroll-to-top on tab tap, PiP avoidance, proper safe
areas
- **Accessibility**: Platform accessibility features work automatically
Reference:
- [React Navigation Native Stack](https://reactnavigation.org/docs/native-stack-navigator)
- [React Native Bottom Tabs with React Navigation](https://oss.callstack.com/react-native-bottom-tabs/docs/guides/usage-with-react-navigation)
- [React Native Bottom Tabs with Expo Router](https://oss.callstack.com/react-native-bottom-tabs/docs/guides/usage-with-expo-router)
- [Expo Router Native Tabs](https://docs.expo.dev/router/advanced/native-tabs)