Skip to content

t1k:rn:base:navigation

FieldValue
Modulebase
Version1.7.4
Effortmedium
Tools

Keywords: deep linking, drawer, expo router, navigation, react native, stack, tab

/t1k:rn:base:navigation

// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { TabBarIcon } from '@/components/navigation/TabBarIcon';
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: '#0a7ea4' }}>
<Tabs.Screen
name="index"
options={{ title: 'Home', tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} /> }}
/>
<Tabs.Screen
name="profile"
options={{ title: 'Profile', tabBarIcon: ({ color }) => <TabBarIcon name="person" color={color} /> }}
/>
</Tabs>
);
}
// Programmatic navigation
import { router, useLocalSearchParams } from 'expo-router';
// Push
router.push('/user/123');
// Replace (no back)
router.replace('/(tabs)');
// Go back
router.back();
// Push with params
router.push({ pathname: '/modal', params: { id: item.id } });
app/user/[id].tsx
export default function UserScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
// id is always string — parse if needed
return <Text>User {id}</Text>;
}
app.json
{
"expo": {
"scheme": "myapp",
"web": { "bundler": "metro" }
}
}
// app/_layout.tsx — present modals over tabs
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
</Stack>
  • Route params are always strings — parse with parseInt/parseFloat as needed
  • Use router.replace() after auth login/logout — prevents back navigation to auth screens
  • Group tabs under (tabs)/ — keeps URL clean
  • Shared screens (e.g., detail) go outside tab group — accessible from any tab
  • useLocalSearchParams returns string | string[] — always use typed generic
  • Back button behavior: Android hardware back works automatically with Expo Router
  • href in <Link> must be typed: import { Href } from 'expo-router'
  • Deep links require expo-linking and scheme configured in app.json