🧭 Navigation Guide (Expo Router)
Estructura de Navegación
app/
├── _layout.tsx # Layout raíz (Stack principal)
├── (tabs)/ # Grupo de tabs
│ ├── _layout.tsx # Configuración de tabs
│ ├── (home)/ # Tab Home
│ │ ├── _layout.tsx # Stack interno
│ │ ├── index.tsx # Pantalla principal
│ │ └── item-detail.tsx # Detalle de item
│ ├── explore/
│ ├── favorites/
│ └── profile/
└── notifications.tsx # Modal (fuera de tabs)
Tipos de Navegación
1. Navegación con Tabs
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
export default function TabLayout() {
return (
<Tabs screenOptions={{ headerShown: false }}>
<Tabs.Screen
name="(home)"
options={{
title: 'Home',
tabBarIcon: ({ color, size }) => <Home size={size} color={color} />,
}}
/>
<Tabs.Screen
name="explore"
options={{
title: 'Explorar',
tabBarIcon: ({ color, size }) => <Compass size={size} color={color} />,
}}
/>
</Tabs>
);
}
2. Stack Interno en Tab
// app/(tabs)/(home)/_layout.tsx
import { Stack } from 'expo-router';
export default function HomeLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen
name="item-detail"
options={{
title: 'Detalle',
headerBackTitle: 'Volver',
}}
/>
</Stack>
);
}
3. Modales (Fuera de Tabs)
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="notifications"
options={{
presentation: 'modal',
title: 'Notificaciones',
}}
/>
</Stack>
);
}
Navegación Programática
useRouter
import { useRouter } from 'expo-router';
function MyComponent() {
const router = useRouter();
// Navegar a una ruta
router.push('/explore');
// Con parámetros
router.push({
pathname: '/item-detail',
params: { id: '123' }
});
// Reemplazar (sin poder volver)
router.replace('/home');
// Go back
router.back();
// Ir a tab específico
router.push('/(tabs)/favorites');
}
Parámetros de Ruta
// En la pantalla de detalle
import { useLocalSearchParams } from 'expo-router';
function DetailScreen() {
const { id, title } = useLocalSearchParams<{
id: string;
title?: string;
}>();
return <Text>Item ID: {id}</Text>;
}
Link Component
import { Link } from 'expo-router';
<Link href="/explore" asChild>
<TouchableOpacity>
<Text>Ir a Explorar</Text>
</TouchableOpacity>
</Link>
// Con parámetros
<Link
href={{ pathname: '/item-detail', params: { id: '123' } }}
asChild
>
<Button title="Ver detalle" />
</Link>
Configuración de Pantallas
Opciones de Stack.Screen
<Stack.Screen
name="screen"
options={{
// Título
title: 'Mi Pantalla',
// Header
headerShown: true,
headerStyle: { backgroundColor: Colors.primary },
headerTintColor: Colors.textInverse,
headerTitleStyle: { fontWeight: 'bold' },
// Botones del header
headerLeft: () => <BackButton />,
headerRight: () => <SettingsButton />,
// Presentación
presentation: 'modal', // 'card' | 'modal' | 'transparentModal'
animation: 'slide_from_right', // 'fade' | 'slide_from_bottom' | etc.
// Gestos
gestureEnabled: true,
fullScreenGestureEnabled: true,
}}
/>
Opciones Dinámicas
// Dentro del componente de la pantalla
import { Stack } from 'expo-router';
function ProductScreen() {
const { name } = useProduct();
return (
<>
<Stack.Screen
options={{
title: name,
headerRight: () => <ShareButton />,
}}
/>
{/* Contenido */}
</>
);
}
Patrones Comunes
Deep Linking
// Navegar a pantalla anidada
router.push('/(tabs)/profile/edit-profile');
// Desde notificación push
const handleNotification = (notification) => {
const { screen, params } = notification.data;
router.push({ pathname: screen, params });
};
Proteger Rutas
// En _layout.tsx
function RootLayoutNav() {
const { user, isLoading } = useAuth();
if (isLoading) {
return <SplashScreen />;
}
return (
<Stack>
{user ? (
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
) : (
<Stack.Screen name="login" options={{ headerShown: false }} />
)}
</Stack>
);
}
Resetear Navegación
// Resetear al inicio después de logout
import { useNavigation, CommonActions } from '@react-navigation/native';
const navigation = useNavigation();
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{ name: '(tabs)' }],
})
);
Tips y Mejores Prácticas
Usar grupos (parenthesis) para organizar sin afectar URLs
Evitar navegación profunda - máximo 3-4 niveles
Usar modales para flujos que no necesitan historial
Precargar datos con React Query antes de navegar
Manejar estado de carga al navegar entre pantallas