Navegación con Expo Router
Navegación y enrutamiento para aplicaciones Expo Router. Para el estilo de pantallas, colores, controles, medios y efectos visuales, usa el skill expo-native-ui; para movimiento y gestos, usa expo-animation.
Referencias
Consulta estos recursos según sea necesario:
referencias/
route-structure.md Convenciones de rutas, rutas dinámicas, grupos, organización de carpetas
tabs.md NativeTabs, migración desde tabs JS, características de iOS 26
toolbar-and-headers.md Encabezados Stack y botones de barra de herramientas, menús, búsqueda (solo iOS)
form-sheet.md Hojas de formulario en expo-router: configuración, pies de página e interacción de fondo.
search.md Barra de búsqueda con encabezados, hook useSearch, patrones de filtrado
zoom-transitions.md Zoom de Apple: transiciones de zoom fluidas con Link.AppleZoom (iOS 18+)
Estilo de código
- Siempre usa kebab-case para nombres de archivos, ej.
comment-card.tsx - Siempre elimina los archivos de ruta antiguos al mover o reestructurar la navegación
- Nunca uses caracteres especiales en nombres de archivos
- Configura tsconfig.json con alias de ruta y prefiere alias sobre importaciones relativas para refactorizaciones.
Rutas
Consulta ./references/route-structure.md para obtener convenciones detalladas sobre rutas.
- Las rutas pertenecen al directorio
app. - Nunca coloques componentes, tipos o utilidades en el directorio app. Esto es un antipatrón.
- Asegúrate de que la aplicación siempre tenga una ruta que coincida con "/", puede estar dentro de una ruta de grupo.
Preferencias de librerías
Colordeexpo-routerpara colores semánticos nativos, no usesPlatformColordirectamente (seguro en tipos, se adapta automáticamente a claro/oscuro). Consultaexpo-native-uipara el patrón completo de paleta de colores.- En SDK 56+, nunca importes directamente de
@react-navigation/*— usaexpo-router/react-navigationen su lugar (cubre@react-navigation/native,/core,/elements,/routers)
Comportamiento
- Prefiere
Stack.SearchBarpara agregar una barra de búsqueda a una pantalla
Navegación
Link
Usa <Link href="/ruta" /> de 'expo-router' para la navegación entre rutas.
import { Link } from 'expo-router';
// Enlace básico
<Link href="/ruta" />
// Envolviendo componentes personalizados
<Link href="/ruta" asChild>
<Pressable>...</Pressable>
</Link>
Siempre que sea posible, incluye un <Link.Preview> para seguir las convenciones de iOS. Agrega menús contextuales y vistas previas con frecuencia para mejorar la navegación.
Stack
- SIEMPRE usa archivos
_layout.tsxpara definir stacks - Usa Stack de 'expo-router/stack' para stacks de navegación nativos
Título de página
Establece el título de la página con Stack.Title:
<Stack.Title>Inicio</Stack.Title>
Menús contextuales
Agrega menús contextuales de presión prolongada a los componentes Link:
import { Link } from "expo-router";
<Link href="/settings" asChild>
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Menu>
<Link.MenuAction
title="Compartir"
icon="square.and.arrow.up"
onPress={handleSharePress}
/>
<Link.MenuAction
title="Bloquear"
icon="nosign"
destructive
onPress={handleBlockPress}
/>
<Link.Menu title="Más" icon="ellipsis">
<Link.MenuAction title="Copiar" icon="doc.on.doc" onPress={() => {}} />
<Link.MenuAction
title="Eliminar"
icon="trash"
destructive
onPress={() => {}}
/>
</Link.Menu>
</Link.Menu>
</Link>;
Vistas previas de enlaces
Usa vistas previas de enlaces con frecuencia para mejorar la navegación:
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
</Link>
La vista previa del enlace se puede usar con menús contextuales.
Modal
Presenta una pantalla como modal:
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
Prefiere esto a crear un componente modal personalizado.
Hoja
Presenta una pantalla como una hoja de formulario dinámica:
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" },
}}
/>
- Usar
contentStyle: { backgroundColor: "transparent" }hace que el fondo sea de vidrio líquido en iOS 26+.
Estructura común de rutas
Un diseño de aplicación estándar con pestañas y stacks dentro de cada pestaña:
app/
_layout.tsx — <NativeTabs />
(index,search)/
_layout.tsx — <Stack />
index.tsx — Lista principal
search.tsx — Vista de búsqueda
// app/_layout.tsx
import { NativeTabs } from "expo-router/unstable-native-tabs";
import { ThemeProvider, DarkTheme, DefaultTheme } from "expo-router/react-navigation";
import { useColorScheme } from "react-native";
export default function Layout() {
const colorScheme = useColorScheme();
return (
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
<NativeTabs>
<NativeTabs.Trigger name="(index)">
<NativeTabs.Trigger.Icon sf="list.dash" md="list" />
<NativeTabs.Trigger.Label>Elementos</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="(search)" role="search" />
</NativeTabs>
</ThemeProvider>
);
}
Crea una ruta de grupo compartida para que ambas pestañas puedan mostrar pantallas comunes:
// app/(index,search)/_layout.tsx
import { Stack } from "expo-router/stack";
import { colors } from "@/theme/colors";
export default function Layout({ segment }) {
const screen = segment.match(/\((.*)\)/)?.[1]!;
const titles: Record<string, string> = { index: "Elementos", search: "Buscar" };
return (
<Stack
screenOptions={{
headerTransparent: true,
headerShadowVisible: false,
headerLargeTitleShadowVisible: false,
headerLargeStyle: { backgroundColor: "transparent" },
headerTitleStyle: { color: colors.label },
headerLargeTitleEnabled: true,
headerBlurEffect: "none",
headerBackButtonDisplayMode: "minimal",
}}
>
<Stack.Screen name={screen} options={{ title: titles[screen] }} />
<Stack.Screen name="i/[id]" options={{ headerLargeTitleEnabled: false }} />
</Stack>
);
}
headerLargeTitleEnabled es el nombre de opción en SDK 56+; SDKs anteriores usan headerLargeTitle, que está obsoleto aguas arriba.
Enviar comentarios
Si encuentras errores, información engañosa o desactualizada en este skill, repórtalo para que Expo pueda mejorar:
npx --yes submit-expo-feedback@latest --category skills --subject "expo-router" "<comentarios accionables>"
Solo envía cuando tengas algo específico y accionable que reportar. Incluye tanto contexto relevante como sea posible. Si un agente de IA falló repetidamente o el usuario tuvo que asumir una tarea de Expo, carga el skill expo-skill-feedback y sigue su flujo eval-candidate en lugar de reutilizar el comando anterior.