Expo Router Navigation
File-based routing idioms for Expo Router - stacks, tabs, params, and imperative navigation.
Busca en todas las páginas de la documentación
File-based routing idioms for Expo Router - stacks, tabs, params, and imperative navigation.
Routes map from the app/ directory. app/index.tsx is the entry route for that segment.
// app/index.tsx
import { Text, View } from "react-native";
export default function HomeScreen() {
return (
<View style={{ flex: 1, padding: 16 }}>
<Text>Home</Text>
</View>
);
}Configure titles and headers in _layout.tsx with Stack.Screen options.
// app/_layout.tsx
import { Stack } from "expo-router";
export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: "Home" }} />
<Stack.Screen name="settings" options={{ title: "Settings" }} />
</Stack>
);
}Declarative navigation with Link - good for list rows and static menus.
import { Link } from "expo-router";
import { Text } from "react-native";
<Link href="/settings">
<Text>Open settings</Text>
</Link>Imperative navigation from handlers after async work or validation.
import { router } from "expo-router";
function onCreated(id: string) {
router.push(`/items/${id}`);
}Dynamic segments live in brackets: app/items/[id].tsx matches /items/42.
// app/items/[id].tsx
import { useLocalSearchParams } from "expo-router";
export default function ItemScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
return <Text>Item {id}</Text>;
}Read path and query params for the focused route. Values may be string | string[] - normalize when needed.
const { id, tab } = useLocalSearchParams<{ id: string; tab?: string }>();
const itemId = Array.isArray(id) ? id[0] : id;Group tab screens under a (tabs) folder with a tabs _layout.
// app/(tabs)/_layout.tsx
import { Tabs } from "expo-router";
export default function TabsLayout() {
return (
<Tabs>
<Tabs.Screen name="index" options={{ title: "Home" }} />
<Tabs.Screen name="profile" options={{ title: "Profile" }} />
</Tabs>
);
}Send unauthenticated users to sign-in without mounting protected UI.
import { Redirect } from "expo-router";
if (!session) return <Redirect href="/sign-in" />;
return <AppShell />;Pop the stack when possible; otherwise route to a safe fallback.
import { router } from "expo-router";
function onBack() {
if (router.canGoBack()) router.back();
else router.replace("/");
}push adds history; replace swaps the current entry - use after login so back does not return to the form.
router.replace("/(tabs)");Present a screen as a modal via stack options (presentation: "modal").
<Stack.Screen name="compose" options={{ presentation: "modal", title: "New" }} />Build hrefs as objects when you need query params without string concatenation.
<Link href={{ pathname: "/search", params: { q: query } }}>
<Text>Search</Text>
</Link>Update the header from inside a screen when the title depends on loaded data.
import { useNavigation } from "expo-router";
import { useLayoutEffect } from "react";
const navigation = useNavigation();
useLayoutEffect(() => {
navigation.setOptions({ title: item?.name ?? "Item" });
}, [navigation, item?.name]);Parentheses folders like (auth) group routes without affecting the URL path.
// app/(auth)/sign-in.tsx -> /sign-in
// app/(auth)/_layout.tsx can wrap auth screens without a URL segmentConfigure scheme in app.json / app.config so /items/1 opens the matching route when the OS delivers a URL.
// app.config: { expo: { scheme: "myapp" } }
// Opens: myapp://items/1 -> app/items/[id].tsxStack versions: React 19.2.3 · React Native 0.86.0 · Expo SDK 57 · Expo Router
Revisado por Chris St. John·Última actualización: 18 jul 2026