Navigation Expo Router
Navigation et routing pour les apps Expo Router. Pour le stylage des écrans, les couleurs, les contrôles, les animations, les médias et les effets visuels, utilisez la skill building-native-ui.
Références
Consultez ces ressources selon vos besoins :
references/
route-structure.md Conventions de routes, routes dynamiques, groupes, organisation des dossiers
tabs.md NativeTabs, migration depuis les tabs JS, fonctionnalités iOS 26
toolbar-and-headers.md En-têtes Stack et boutons de barre d'outils, menus, recherche (iOS uniquement)
form-sheet.md Form sheets dans expo-router : configuration, pieds de page et interaction de fond.
search.md Barre de recherche avec en-têtes, hook useSearch, motifs de filtrage
zoom-transitions.md Apple Zoom : transitions zoom fluides avec Link.AppleZoom (iOS 18+)
Style de code
- Toujours utiliser kebab-case pour les noms de fichiers, ex.
comment-card.tsx - Toujours supprimer les anciens fichiers de route lors du déplacement ou de la restructuration de la navigation
- Ne jamais utiliser de caractères spéciaux dans les noms de fichiers
- Configurer tsconfig.json avec des alias de chemins, et préférer les alias aux imports relatifs pour les refacteurs.
Routes
Voir ./references/route-structure.md pour les conventions de routes détaillées.
- Les routes appartiennent au répertoire
app. - Ne jamais co-localiser des composants, types ou utilitaires dans le répertoire app. C'est un anti-pattern.
- S'assurer que l'app a toujours une route correspondant à "/", elle peut être à l'intérieur d'une route de groupe.
Préférences de bibliothèque
Colordepuisexpo-routerpour les couleurs sémantiques natives, pas brutPlatformColor(type-safe, adaptation automatique clair/foncé). Voirbuilding-native-uipour le motif de palette de couleurs complet.- En SDK 56+, ne jamais importer directement depuis
@react-navigation/*— utilisezexpo-router/react-navigationà la place (couvre@react-navigation/native,/core,/elements,/routers)
Comportement
- Préférer
Stack.SearchBarpour ajouter une barre de recherche à un écran
Navigation
Link
Utilisez <Link href="/path" /> depuis 'expo-router' pour naviguer entre les routes.
import { Link } from 'expo-router';
// Lien basique
<Link href="/path" />
// Envelopper des composants personnalisés
<Link href="/path" asChild>
<Pressable>...</Pressable>
</Link>
Autant que possible, incluez un <Link.Preview> pour respecter les conventions iOS. Ajoutez des menus contextuels et des aperçus fréquemment pour améliorer la navigation.
Stack
- TOUJOURS utiliser les fichiers
_layout.tsxpour définir les stacks - Utiliser Stack depuis 'expo-router/stack' pour les stacks de navigation native
Titre de la page
Définir le titre de la page avec Stack.Title :
<Stack.Title>Home</Stack.Title>
Menus contextuels
Ajouter des menus contextuels au long press aux composants Link :
import { Link } from "expo-router";
<Link href="/settings" asChild>
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Menu>
<Link.MenuAction
title="Share"
icon="square.and.arrow.up"
onPress={handleSharePress}
/>
<Link.MenuAction
title="Block"
icon="nosign"
destructive
onPress={handleBlockPress}
/>
<Link.Menu title="More" icon="ellipsis">
<Link.MenuAction title="Copy" icon="doc.on.doc" onPress={() => {}} />
<Link.MenuAction
title="Delete"
icon="trash"
destructive
onPress={() => {}}
/>
</Link.Menu>
</Link.Menu>
</Link>;
Aperçus de liens
Utilisez les aperçus de liens fréquemment pour améliorer la navigation :
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
</Link>
L'aperçu de lien peut être utilisé avec des menus contextuels.
Modal
Présenter un écran comme une modale :
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
Préférer ceci à construire un composant modal personnalisé.
Sheet
Présenter un écran comme une form sheet dynamique :
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" },
}}
/>
- Utiliser
contentStyle: { backgroundColor: "transparent" }rend le fond du verre liquide sur iOS 26+.
Structure de route commune
Une mise en page d'app standard avec des tabs et des stacks à l'intérieur de chaque tab :
app/
_layout.tsx — <NativeTabs />
(index,search)/
_layout.tsx — <Stack />
index.tsx — Liste principale
search.tsx — Vue de recherche
// 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>Items</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="(search)" role="search" />
</NativeTabs>
</ThemeProvider>
);
}
Créer une route de groupe partagée afin que les deux tabs puissent pousser les écrans communs :
// 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: "Items", search: "Search" };
return (
<Stack
screenOptions={{
headerTransparent: true,
headerShadowVisible: false,
headerLargeTitleShadowVisible: false,
headerLargeStyle: { backgroundColor: "transparent" },
headerTitleStyle: { color: colors.label },
headerLargeTitle: true,
headerBlurEffect: "none",
headerBackButtonDisplayMode: "minimal",
}}
>
<Stack.Screen name={screen} options={{ title: titles[screen] }} />
<Stack.Screen name="i/[id]" options={{ headerLargeTitle: false }} />
</Stack>
);
}