| name | mobile-navigation-setup |
|---|---|
| description | Set up file-based navigation in an Expo Router project. Covers tab layouts, stack navigation, drawer navigation, typed routes, dynamic segments, deep linking, and layout composition. Use when the user wants to add screens, tabs, or navigation flows. |
| standards-version | 1.6.3 |
Use this skill when the user:
- Wants to add tabs, a drawer, or stack navigation to an Expo app
- Asks how Expo Router file-based routing works
- Needs to add a screen or route group
- Wants to set up deep linking or typed routes
- Mentions "navigation", "tabs", "stack", "drawer", "routes", or "linking"
- Navigation type: tabs, stack, drawer, or a combination
- Screen list: which screens/tabs the user wants
- Deep link scheme (optional): custom URL scheme for deep linking
-
Confirm Expo Router is installed. It ships with
create-expo-appby default. If the project was set up manually:npx expo install expo-router expo-linking expo-constants
-
Explain the
app/directory convention. Expo Router uses file-system routing:app/ ├── _layout.tsx # Root layout (wraps everything) ├── index.tsx # "/" route (home) ├── +not-found.tsx # Catch-all 404 ├── (tabs)/ # Tab group │ ├── _layout.tsx # Tab bar configuration │ ├── index.tsx # First tab │ ├── search.tsx # Second tab │ └── profile.tsx # Third tab ├── settings/ # Stack group │ ├── _layout.tsx # Stack navigator config │ ├── index.tsx # Settings home │ └── [id].tsx # Dynamic route: /settings/123 └── modal.tsx # Modal screen (presented modally via root layout)Key conventions:
_layout.tsxfiles define the navigator for that directory- Parenthesized directories like
(tabs)are route groups (do not appear in the URL) [param].tsxfiles are dynamic segments+not-found.tsxis the 404 handler
-
Create the root layout. The root
_layout.tsxwraps the entire app:import { Stack } from "expo-router"; export default function RootLayout() { return ( <Stack> <Stack.Screen name="(tabs)" options={{ headerShown: false }} /> <Stack.Screen name="modal" options={{ presentation: "modal" }} /> </Stack> ); }
-
Create a tab layout. Inside
app/(tabs)/_layout.tsx:import { Tabs } from "expo-router"; import { Ionicons } from "@expo/vector-icons"; export default function TabLayout() { return ( <Tabs screenOptions={{ tabBarActiveTintColor: "#007AFF" }}> <Tabs.Screen name="index" options={{ title: "Home", tabBarIcon: ({ color, size }) => ( <Ionicons name="home" size={size} color={color} /> ), }} /> <Tabs.Screen name="search" options={{ title: "Search", tabBarIcon: ({ color, size }) => ( <Ionicons name="search" size={size} color={color} /> ), }} /> <Tabs.Screen name="profile" options={{ title: "Profile", tabBarIcon: ({ color, size }) => ( <Ionicons name="person" size={size} color={color} /> ), }} /> </Tabs> ); }
-
Set up typed routes. Enable typed routes in
app.jsonfor autocompletion and type safety:{ "expo": { "experiments": { "typedRoutes": true } } }Then generate types:
npx expo customize tsconfig.json
Usage with typed routes:
import { useRouter } from "expo-router"; const router = useRouter(); router.push("/settings/42"); // Typed and validated router.push("/(tabs)/profile"); // Group paths work too
-
Dynamic segments. For routes like
/settings/[id]:import { useLocalSearchParams } from "expo-router"; export default function SettingsDetail() { const { id } = useLocalSearchParams<{ id: string }>(); return <Text>Setting: {id}</Text>; }
-
Deep linking. Set the
schemeinapp.json:{ "expo": { "scheme": "myapp" } }This enables
myapp://settings/42to open the dynamic route. Expo Router handles the mapping automatically because routes mirror the file system. -
Generate screens using MCP. Use the
mobile_generateScreentool for quick scaffolding:mobile_generateScreen(name: "profile", type: "tab", layout_group: "(tabs)")
- Expo Router: File-based routing
- Expo Router: Tabs
- Expo Router: Stack
- Expo Router: Typed routes
- Expo Router: Deep linking
User: "I want my app to have a tab bar with Home, Search, and Profile tabs, plus a Settings screen that opens as a stack."
Agent:
- Checks that Expo Router is installed
- Creates
app/(tabs)/_layout.tsxwith three tab screens - Creates
app/(tabs)/index.tsx,search.tsx,profile.tsx - Creates
app/settings/_layout.tsxas a Stack - Creates
app/settings/index.tsx - Updates root
app/_layout.tsxto include both groups - Enables typed routes in
app.json
| Step | MCP Tool | Description |
|---|---|---|
| Scaffold a screen | mobile_generateScreen |
Create a new screen file with correct Expo Router convention |
| Install nav packages | mobile_installDependency |
Install expo-router or related packages via npx expo install |
| Verify environment | mobile_checkDevEnvironment |
Confirm Expo CLI and Node are available |
- Forgetting
_layout.tsx- Every route group needs a layout file. Without it, screens in that directory will not render. - Nesting navigators manually - Expo Router replaces manual
NavigationContainersetup. Do not import from@react-navigation/nativedirectly. - Using
index.tsxas a catch-all -index.tsxonly matches the exact path of its parent directory, not nested paths. - Group names in URLs - Parenthesized groups like
(tabs)do not appear in the URL./(tabs)/profileis accessed as/profileexternally. - Missing
schemefor deep links - Deep linking will not work without settingschemeinapp.json. It also needs to be unique across apps on the device. - Drawer navigator not installed -
expo-routerships Stack and Tabs. For drawers, install@react-navigation/drawerandreact-native-gesture-handler.
- Mobile Project Setup - create the Expo project before adding navigation
- Mobile Component Patterns - build reusable components to use inside screens