Conceptos básicos de Expo Router
10 ejemplos para comenzar con Expo Router: 7 básicos e intermedios. Rutas basadas en archivos, _layout.tsx, y el modelo mental frente a React Navigation.
Busca en todas las páginas de la documentación
10 ejemplos para comenzar con Expo Router: 7 básicos e intermedios. Rutas basadas en archivos, _layout.tsx, y el modelo mental frente a React Navigation.
Crea un andamiaje de una app SDK 57 con la plantilla predeterminada: incluye Expo Router, TypeScript, y el punto de entrada recomendado.
npx create-expo-app@latest MyRouterApp --template default@sdk-57
cd MyRouterApp
npm install
npx expo install expo-routerConfirma el punto de entrada del router y el SDK fijado:
{
"main": "expo-router/entry",
"dependencies": {
"expo": "~57.0.4",
"expo-router": "~6.0.8",
"react": "19.2.3",
"react-native": "0.86.0"
}
}Herramientas: Estos ejemplos tienen como destino Expo SDK 57 (
expo~57.0.4), React Native 0.86.0, y React 19.2.3.
Cada archivo bajo app/ se convierte en un segmento de ruta. El nombre del archivo es la ruta, sin necesidad de configuración linking central para las pantallas estándar.
app/
├── index.tsx → /
├── settings.tsx → /settings
├── profile/
│ └── index.tsx → /profile
└── orders/
├── index.tsx → /orders
└── [id].tsx → /orders/:id// app/settings.tsx
import { Text, View } from "react-native";
export default function SettingsScreen() {
return (
<View style={{ flex: 1, padding: 16 }}>
<Text>Configuración</Text>
</View>
);
}app/index.tsx es la ruta /: la pantalla de aterrizaje predeterminada de la appapp/profile/index.tsx → /profile)[id].tsx es un segmento dinámico: accede a id mediante useLocalSearchParams()features/ cuando la lógica creceRelacionado: Grupos de rutas y organización de
app/-(auth)y(tabs)sin cambiar URLs | ../expo-rules/navigation-and-routing-rules/navigation-and-routing-rules.md - convenciones de enrutamiento del equipo
_layout.tsx es propietario del navegadorLos archivos _layout.tsx definen navegadores (Stack, Tabs, Drawer) para su directorio. El layout raíz envuelve los proveedores globales y exporta el navegador de nivel superior.
// app/_layout.tsx
import { Stack } from "expo-router";
import { StatusBar } from "expo-status-bar";
export default function RootLayout() {
return (
<>
<StatusBar style="auto" />
<Stack>
<Stack.Screen name="index" options={{ title: "Inicio" }} />
<Stack.Screen name="settings" options={{ title: "Configuración" }} />
</Stack>
</>
);
}Stack de expo-router es un envoltorio basado en archivos alrededor de @react-navigation/native-stack<Stack.Screen name="settings"> configura la pantalla que coincide con app/settings.tsxapp/orders/[id].tsx se envía al stack raíz a menos que un _layout.tsx anidado diga lo contrarioRelacionado: Navegación Stack - encabezados,
screenOptions, push/pop | ../architecture-design/feature-sliced-design-for-rn/feature-sliced-design-for-rn.md - manténapp/como la capa de app FSD
React Navigation construye un árbol de navegador en código. Expo Router construye el mismo árbol desde el sistema de archivos: cambias createNativeStackNavigator imperativo por nombres de archivo declarativos.
// React Navigation (árbol imperativo)
const Stack = createNativeStackNavigator();
function AppNavigator() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
);
}// Expo Router (árbol del sistema de archivos)
app/
├── _layout.tsx → navegador Stack para esta carpeta
├── index.tsx → pantalla "Inicio" (nombre derivado del archivo)
└── settings.tsx → pantalla "Configuración"| Concepto | React Navigation | Expo Router |
|---|---|---|
| Registro de pantalla | <Stack.Screen name="Settings" /> | existe app/settings.tsx |
| Navegador anidado | Navegador dentro de una pantalla | app/(tabs)/_layout.tsx |
| Ruta de enlace profundo | Configuración linking manual | Ruta de archivo ≈ ruta URL |
| Opciones de pantalla | prop options en Screen | export const options o <Stack.Screen options> |
useNavigation, useFocusEffect y los APIs de encabezado siguen siendo aplicablessettings.tsx se registra como "settings" en el stackRelacionado: Link & href - navegación declarativa sin memorizar nombres de rutas | Typed Routes - seguridad de rutas en tiempo de compilación
expo-router/entryEl main de package.json apunta Metro a la entrada de Expo Router en lugar de expo/AppEntry.js o un App.tsx escrito a mano.
{
"main": "expo-router/entry",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios"
}
}// No necesitas App.tsx - la entrada carga app/_layout.tsx automáticamente
// app/_layout.tsx es la verdadera raíz de la aplicación
import { Slot } from "expo-router";
export default function RootLayout() {
return <Slot />;
}expo-router/entry registra el componente raíz, los manejadores de enlaces, y los límites de erroresSlot renderiza la ruta secundaria coincidente: úsalo cuando desees un layout sin forzar un envoltorio Stack/Tabsmain: "expo-router/entry" interrumpe el enrutamiento: el bundler no descubrirá las rutas app/npx expo install expo-router asegura que la versión del router coincida con los pares de React Navigation del SDK 57Relacionado: ../project-setup/project-setup-basics/project-setup-basics.md - pin del SDK y layout de plantilla predeterminada
La sintaxis de corchetes crea segmentos de URL dinámicos. Lee los parámetros con useLocalSearchParams: valida antes de usar.
// app/orders/[id].tsx
import { useLocalSearchParams } from "expo-router";
import { Text, View } from "react-native";
export default function OrderDetailScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
return (
<View style={{ flex: 1, padding: 16 }}>
<Text>Pedido #{id}</Text>
</View>
);
}// Navega programáticamente (imperativo)
import { router } from "expo-router";
router.push({ pathname: "/orders/[id]", params: { id: "42" } });[id] coincide con un segmento: /orders/42 establece id: "42" (siempre cadenas de URLs)app/users/[userId]/posts/[postId].tsx expone ambos parámetrosRelacionado: Typed Routes - tipos
Hrefgenerados para navegación segura de parámetros | ../expo-rules/navigation-and-routing-rules/navigation-and-routing-rules.md - reglas de validación de parámetros
index.tsx dentro de una carpeta es la pantalla predeterminada para ese segmento de ruta, como index.html en la web.
app/
├── index.tsx → /
└── orders/
├── _layout.tsx → Stack para /orders/*
├── index.tsx → /orders (lista)
└── [id].tsx → /orders/:id (detalle)// app/orders/index.tsx
import { Link } from "expo-router";
import { Text, View } from "react-native";
const ORDERS = [{ id: "1" }, { id: "2" }];
export default function OrdersListScreen() {
return (
<View style={{ flex: 1, padding: 16, gap: 12 }}>
{ORDERS.map((order) => (
<Link key={order.id} href={{ pathname: "/orders/[id]", params: { id: order.id } }}>
Pedido #{order.id}
</Link>
))}
</View>
);
}/orders se resuelve en orders/index.tsx, no en orders.tsx: elige un patrón por característica_layout.tsx anidado envuelve todos los hermanos en esa carpeta (index.tsx y [id].tsx)Relacionado: Redirects & Index Routes - entrada de aterrizaje predeterminada y con puerta de auth | Link & href -
Linkvsrouter.push
Exporta options desde un archivo de ruta o configura pantallas en _layout.tsx: ambos alimentan el API de opciones de pantalla de React Navigation.
// app/settings.tsx
import { Stack } from "expo-router";
import { Text, View } from "react-native";
export const options = {
title: "Configuración",
headerLargeTitle: true,
};
export default function SettingsScreen() {
return (
<View style={{ flex: 1, padding: 16 }}>
<Text>Configuración</Text>
</View>
);
}// app/_layout.tsx (alternativa - centraliza el chrome)
import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Stack
screenOptions={{
headerShown: true,
animation: "slide_from_right",
}}
/>
);
}screenOptions en el navegador establece valores predeterminados; options por archivo reemplaza para una pantallaStack.Screen name="settings" options={{...}} en _layout.tsx es equivalente a exportar options de settings.tsxuseNavigation().setOptions() dentro de la pantalla cuando se cargan los datosRelacionado: Stack Navigation - receta de encabezados, gestos, y modos de presentación
La plantilla predeterminada del SDK 57 anida stacks bajo pestañas: cada carpeta de pestaña puede enviar pantallas de detalle sin perder la barra de pestañas.
app/
├── _layout.tsx
└── (tabs)/
├── _layout.tsx → Navegador Tabs
├── index.tsx → Pestaña Inicio
└── orders/
├── _layout.tsx → Stack dentro de la pestaña Pedidos
├── index.tsx → /orders lista (raíz de pestaña)
└── [id].tsx → /orders/:id (enviado, barra de pestañas permanece)// app/(tabs)/_layout.tsx
import { Tabs } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
export default function TabLayout() {
return (
<Tabs screenOptions={{ headerShown: false }}>
<Tabs.Screen
name="index"
options={{ title: "Inicio", tabBarIcon: ({ color }) => <Ionicons name="home" size={24} color={color} /> }}
/>
<Tabs.Screen
name="orders"
options={{ title: "Pedidos", tabBarIcon: ({ color }) => <Ionicons name="list" size={24} color={color} /> }}
/>
</Tabs>
);
}// app/(tabs)/orders/_layout.tsx
import { Stack } from "expo-router";
export default function OrdersStackLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: "Pedidos" }} />
<Stack.Screen name="[id]" options={{ title: "Pedido" }} />
</Stack>
);
}(tabs)/_layout.tsx: cada carpeta o archivo secundario inmediato se convierte en una pestañaorders/: envía [id] encima de orders/index mientras la barra de pestañas permanece visible(tabs) son un grupo de rutas: organizan archivos sin agregar /tabs a la URLRelacionado: Tabs & Drawers - estilo de barra de pestañas, composición de cajón | Route Groups & Organizing
app/- convenciones de(tabs)
Los grupos de rutas (auth) y (app) separan los flujos de inicio de sesión del shell principal sin cambiar las rutas de URL.
app/
├── _layout.tsx
├── index.tsx → Hub de redirección (consulta artículo de redirecciones)
├── (auth)/
│ ├── _layout.tsx → Stack para inicio de sesión/registro
│ ├── login.tsx → /login
│ └── register.tsx → /register
└── (app)/
├── _layout.tsx → Shell autenticado (pestañas o stack)
└── (tabs)/
├── _layout.tsx
└── index.tsx → / (cuando inicia sesión)// app/(auth)/_layout.tsx
import { Stack } from "expo-router";
export default function AuthLayout() {
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="login" />
<Stack.Screen name="register" />
</Stack>
);
}(auth) y (app) no aparecen en las URLs: /login, no /auth/login_layout.tsx raíz o index.tsx con <Redirect />: consulta el libro de recetas de redirecciones_layout.tsxRelacionado: Redirects & Index Routes - entrada con puerta de auth | ../auth-session/mobile-auth-basics/mobile-auth-basics.md - proveedores de sesión
Las apps de producción mantienen app/ como una capa de enrutamiento: las pantallas, hooks, y llamadas de API viven en features/.
features/
└── orders/
├── index.ts
├── ui/
│ ├── OrdersListScreen.tsx
│ └── OrderDetailScreen.tsx
└── model/
└── useOrder.ts
app/
└── (tabs)/
└── orders/
├── index.tsx → solo re-exporta
└── [id].tsx → solo re-exporta// features/orders/index.ts
export { OrdersListScreen } from "./ui/OrdersListScreen";
export { OrderDetailScreen } from "./ui/OrderDetailScreen";// app/(tabs)/orders/index.tsx
export { OrdersListScreen as default } from "@/features/orders";// app/(tabs)/orders/[id].tsx
export { OrderDetailScreen as default } from "@/features/orders";index.ts: sin importaciones profundas entre característicasapp/ es la capa de app, no una carpeta de diosRelacionado: Expo Router Fundamentals Best Practices - resumen de sección completa | ../project-setup/folder-structure-for-features/folder-structure-for-features.md - escalado más allá del árbol de inicio
Versiones de Stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0, y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 16 jul 2026