agents-inc/skills/src/skills/mobile-navigation-expo-router/SKILL.md
mobile-navigation-expo-router
File-based routing and navigation for Expo/React Native
- Source repository stars
- 23
- Declared platforms
- 0
- Static risk flags
- 0
- Last source update
- 2026-08-09
- Source checked
- 2026-08-28
Decision brief
What it does: where it fits
Quick Guide: File-based routing for React Native and web. Files in app/ become routes automatically. Use layout.tsx for navigation structure (Stack, Tabs), groups (name)/ for URL-invisible organization, [param] for dynamic segments. SDK 53+: use Stack.Protected with a guard prop…
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/agents-inc/skills --skill "src/skills/mobile-navigation-expo-router"Inspect the Agent Skill "mobile-navigation-expo-router" from https://github.com/agents-inc/skills/blob/81d43a51211aca12c85dcc16085fa99014ec548e/src/skills/mobile-navigation-expo-router/SKILL.md at commit 81d43a51211aca12c85dcc16085fa99014ec548e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
Setting up file-based navigation in an Expo appImplementing authentication flows with route protectionCreating tab, stack, or modal navigation layouts - 02
Philosophy
Expo Router maps the filesystem to your navigation hierarchy. Every file in app/ is a route; every layout.tsx defines how its sibling routes are presented (stack, tabs, drawer). This convention-over-configuration approach means:
URLs are first-class -- every screen has a URL, enabling deep linking on mobile and SEO on web without extra configurationLayouts are composable -- nest layout.tsx files to create any navigation structure (tabs containing stacks containing modals)The file tree IS the sitemap -- new developers understand navigation by reading the directory structure, not a central config - 03
Core Patterns
Every file in app/ maps to a route. Special characters change behavior:
examples/core.md - Directory structure, layouts, tabs, navigation hooks, typed routes, modalsexamples/auth.md - Stack.Protected pattern, SessionProvider, legacy redirect patternexamples/api-routes.md - API route handlers, error handling, deployment - 04
Pattern 1: File Conventions
Every file in app/ maps to a route. Special characters change behavior:
Every file in app/ maps to a route. Special characters change behavior:Key insight: Groups (name)/ are purely organizational. (tabs)/home.tsx and home.tsx both resolve to /home. Use groups to apply different layouts to different route sets without changing URLs.Full directory structure examples: examples/core.md - 05
Pattern 2: Layout Routes
layout.tsx files wrap their sibling routes in a navigator. The layout determines HOW routes are presented (stack push, tab switch, modal overlay).
layout.tsx files wrap their sibling routes in a navigator. The layout determines HOW routes are presented (stack push, tab switch, modal overlay).Why this matters: Without a layout.tsx, routes get a default Stack navigator with default headers. Always define layouts explicitly for control over headers, transitions, and navigation structure.Gotcha: The name prop in Stack.Screen/Tabs.Screen must match the filename (without extension) or directory name exactly. name="(tabs)" matches the (tabs)/ directory.
Permission review
Static risk signals and limitations
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 91/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 23 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- agents-inc/skills
- Skill path
- src/skills/mobile-navigation-expo-router/SKILL.md
- Commit
- 81d43a51211aca12c85dcc16085fa99014ec548e
- License
- MIT
- Collected
- 2026-08-28
- Default branch
- main
View the original SKILL.md
Expo Router Patterns
Quick Guide: File-based routing for React Native and web. Files in
app/become routes automatically. Use_layout.tsxfor navigation structure (Stack, Tabs), groups(name)/for URL-invisible organization,[param]for dynamic segments. SDK 53+: useStack.Protectedwith aguardprop for authentication. EnabletypedRoutesfor compile-time route safety. API routes use+api.tssuffix.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST define navigation structure in _layout.tsx files -- screens without a layout parent default to a basic Stack)
(You MUST use Stack.Protected with guard prop for authentication flows in SDK 53+ -- NOT imperative redirects in useEffect)
(You MUST use useLocalSearchParams for route params in screens -- useGlobalSearchParams causes unnecessary re-renders on unfocused screens)
(You MUST enable typedRoutes in app.json experiments for compile-time route validation -- catches invalid navigation at build time)
</critical_requirements>
Auto-detection: expo-router, Expo Router, file-based routing, _layout.tsx, Stack.Screen, Tabs.Screen, useRouter, useLocalSearchParams, useSegments, usePathname, Link href, router.push, router.replace, router.dismiss, router.dismissTo, +api.ts, +not-found, Stack.Protected, generateStaticParams, expo-router/head, Slot, Redirect, useFocusEffect, NativeTabs, headless tabs, TabSlot, TabTrigger
When to use:
- Setting up file-based navigation in an Expo app
- Implementing authentication flows with route protection
- Creating tab, stack, or modal navigation layouts
- Building API routes for server-side logic
- Configuring typed routes for compile-time safety
- Adding deep linking and static rendering for web
Key patterns covered:
- File convention:
_layout.tsx,[param],[...slug],(group)/,+api.ts,+not-found.tsx - Layout navigators: Stack, Tabs, headless tabs, native tabs
- Authentication:
Stack.Protectedguard pattern (SDK 53+), redirect pattern (SDK 52) - Navigation hooks:
useRouter,useLocalSearchParams,useSegments,usePathname - API routes with standard Request/Response
- Typed routes with auto-generated TypeScript definitions
- Modal routes, shared routes between tabs, nested navigation
When NOT to use:
- Apps that need fully custom native navigation controllers beyond what React Navigation provides
- Simple single-screen apps with no navigation
- Web-only projects where a web-native router is more appropriate
Philosophy
Expo Router maps the filesystem to your navigation hierarchy. Every file in app/ is a route; every _layout.tsx defines how its sibling routes are presented (stack, tabs, drawer). This convention-over-configuration approach means:
- URLs are first-class -- every screen has a URL, enabling deep linking on mobile and SEO on web without extra configuration
- Layouts are composable -- nest
_layout.tsxfiles to create any navigation structure (tabs containing stacks containing modals) - The file tree IS the sitemap -- new developers understand navigation by reading the directory structure, not a central config
- Universal by default -- the same route definitions work on iOS, Android, and web
Mental model: Think of app/ as a website. _layout.tsx files are the "chrome" (nav bars, tab bars). Route files are the "pages." Groups (name)/ organize without affecting URLs. This maps directly to how web routing works, which is intentional -- Expo Router is built on top of React Navigation but presents a web-like API.
Core Patterns
Pattern 1: File Conventions
Every file in app/ maps to a route. Special characters change behavior:
| File | URL | Purpose |
|---|---|---|
index.tsx | / (or parent path) | Default route for directory |
about.tsx | /about | Static route |
[id].tsx | /:id | Dynamic segment |
[...slug].tsx | /a/b/c | Catch-all segments |
_layout.tsx | N/A | Wraps sibling routes in navigator |
(group)/ | Not in URL | Organizes routes without URL impact |
+not-found.tsx | N/A | 404 fallback for unmatched routes |
+api.ts | Server endpoint | API route handler |
+html.tsx | N/A | Root HTML wrapper (web static rendering) |
Key insight: Groups (name)/ are purely organizational. (tabs)/home.tsx and home.tsx both resolve to /home. Use groups to apply different layouts to different route sets without changing URLs.
Full directory structure examples: examples/core.md
Pattern 2: Layout Routes
_layout.tsx files wrap their sibling routes in a navigator. The layout determines HOW routes are presented (stack push, tab switch, modal overlay).
// app/_layout.tsx -- Root layout wrapping 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.Screen name="+not-found" />
</Stack>
);
}
Why this matters: Without a _layout.tsx, routes get a default Stack navigator with default headers. Always define layouts explicitly for control over headers, transitions, and navigation structure.
Gotcha: The name prop in Stack.Screen/Tabs.Screen must match the filename (without extension) or directory name exactly. name="(tabs)" matches the (tabs)/ directory.
Full layout examples (tabs, nested stacks, drawers): examples/core.md
Pattern 3: Navigation Hooks
import {
useRouter,
useLocalSearchParams,
usePathname,
useSegments,
} from "expo-router";
// useRouter -- imperative navigation
const router = useRouter();
router.push("/users/123"); // Add to stack
router.replace("/home"); // Replace current (no back)
router.back(); // Go back
router.dismiss(); // Pop one screen in nearest stack
router.dismissTo("/home"); // Pop until reaching /home
router.dismissAll(); // Pop to first screen in stack
router.canGoBack(); // Check if back is possible
router.canDismiss(); // Check if dismiss is possible
router.prefetch("/heavy-screen"); // Preload in background
// useLocalSearchParams -- route params for focused screen only
const { id } = useLocalSearchParams<{ id: string }>();
// usePathname -- current path without query params
const pathname = usePathname(); // "/users/123"
// useSegments -- raw file segments of current route
const segments = useSegments(); // ["users", "[id]"]
Critical: Use useLocalSearchParams over useGlobalSearchParams. The global variant re-renders the component whenever ANY route's params change -- even when the screen is unfocused in the background. Local only updates when the screen is focused.
Full hook usage examples: examples/core.md
Pattern 4: Authentication with Stack.Protected (SDK 53+)
The recommended pattern uses Stack.Protected with a guard prop to declaratively show/hide routes based on auth state.
// app/_layout.tsx
import { Stack } from "expo-router";
import { useSession } from "../ctx";
function RootNavigator() {
const { session } = useSession();
return (
<Stack>
<Stack.Protected guard={!!session}>
<Stack.Screen name="(app)" />
</Stack.Protected>
<Stack.Protected guard={!session}>
<Stack.Screen name="sign-in" />
</Stack.Protected>
</Stack>
);
}
How guard works: When guard is false, the screens inside are inaccessible. If a user tries to navigate to a protected screen, or a screen becomes protected while active, they are redirected to the first available unprotected screen.
Gotcha: All routes remain defined and accessible in the file system. Stack.Protected controls runtime accessibility, not build-time elimination. Deep links to protected routes trigger redirects to the sign-in screen.
Full auth pattern with SessionProvider and splash screen: examples/auth.md Legacy redirect pattern (SDK 52): examples/auth.md
Pattern 5: Modal Routes
Modals are defined as regular route files but configured with presentation: "modal" in the parent layout.
// app/_layout.tsx
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="modal"
options={{
presentation: "modal",
headerShown: true,
title: "Settings",
}}
/>
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetCornerRadius: 16,
}}
/>
</Stack>
Key insight: Modals sit outside tab groups so they overlay the entire app. Navigation to a modal from any tab: router.push("/modal"). Dismiss with router.back() or router.dismiss().
Full modal examples: examples/core.md
Pattern 6: API Routes
Files with +api.ts suffix define server-side endpoints. They use standard Web Request/Response APIs.
// app/api/users+api.ts
export async function GET(request: Request) {
const users = await db.users.findMany();
return Response.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.users.create(body);
return Response.json(user, { status: 201 });
}
Requires web.output: "server" in app.json. For native apps, set origin in the expo-router plugin config to point to your deployed server.
Limitation: API routes bundle to CommonJS, no dynamic imports, no platform-specific extensions (+api.web.ts does not work).
Full API route examples with error handling: examples/api-routes.md
Pattern 7: Typed Routes
Enable compile-time route validation by setting experiments.typedRoutes: true in app.json. The dev server auto-generates type definitions.
// With typedRoutes enabled:
router.push("/about"); // OK
router.push("/nonexistent"); // TypeScript error
router.push({
pathname: "/users/[id]",
params: { id: "123" }, // Typed params required
});
// Typed search params
const { id } = useLocalSearchParams<"/users/[id]">();
// id is typed as string
Gotcha: Generated types are git-ignored. CI pipelines need npx expo customize tsconfig.json to regenerate types before type-checking. Relative paths are not supported -- always use absolute paths.
Typed routes setup and examples: examples/core.md
Pattern 8: Static Rendering and Head Metadata (Web)
Static rendering generates HTML at build time for SEO and fast initial loads.
// app.json: { "web": { "output": "static" } }
// app/about.tsx
import Head from "expo-router/head";
import { Text } from "react-native";
export default function AboutPage() {
return (
<>
<Head>
<title>About Us</title>
<meta name="description" content="Learn about our company" />
</Head>
<Text>About page content</Text>
</>
);
}
For dynamic routes, export generateStaticParams to pre-render pages at build time:
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({ id: post.id }));
}
Full static rendering and Head examples: examples/web.md
Detailed Resources:
- examples/core.md - Directory structure, layouts, tabs, navigation hooks, typed routes, modals
- examples/auth.md - Stack.Protected pattern, SessionProvider, legacy redirect pattern
- examples/api-routes.md - API route handlers, error handling, deployment
- examples/web.md - Static rendering, Head metadata, root HTML
- reference.md - Decision frameworks, version compatibility
<decision_framework>
Decision Frameworks
Expo Router provides multiple navigation patterns. The key decisions:
- Route type -- static, dynamic, catch-all, grouped, API? See reference.md for the full route type decision tree.
- Navigation method -- declarative
<Link>vs imperativerouter.push/replace/dismiss? See reference.md for the navigation method decision tree. - Layout navigator -- Stack, Tabs, NativeTabs, headless tabs, or
<Slot />? See reference.md for the layout navigator selection guide. - Hook choice --
useLocalSearchParamsvsuseGlobalSearchParams,useRoutervs<Link>,useFocusEffectvsuseEffect? See reference.md for the hook selection table.
Quick rules:
- Prefer
<Link>for static navigation in UI,router.pushfor programmatic navigation in event handlers - Always use
useLocalSearchParamsunless you specifically need background screen updates - Use
useFocusEffectinstead ofuseEffectwhen data should refresh on screen focus
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Using
useGlobalSearchParamswhenuseLocalSearchParamsworks -- global causes re-renders on ALL route changes, even when screen is in background; use local for screen-specific params - Imperative redirects in useEffect for auth (SDK 53+) -- use
Stack.Protectedwithguardprop instead; it's declarative, handles edge cases, and integrates with deep linking correctly - Missing
_layout.tsxin route groups -- without a layout, the default Stack has default headers and no control over transitions; always define layouts explicitly - Storing secrets in API route responses without authentication -- API routes are public endpoints; validate authentication tokens before returning sensitive data
Medium Priority Issues:
nameprop mismatch in layout screens --Stack.Screen name="tabs"does not match directory(tabs)/; must bename="(tabs)"exactly- Not using
presentation: "modal"in parent layout -- configuring modal in the modal file's own layout does nothing; modals must be configured in the parent navigator - Calling
router.replacein initial render -- causes navigation before the navigator is ready; useRedirectcomponent oruseFocusEffectinstead
Gotchas & Edge Cases:
- Deep links to protected routes:
Stack.Protectedredirects to the first unprotected screen -- deep link target is lost unless you store and replay it after auth - Catch-all
[...slug]params: Always an array, butuseLocalSearchParamsmay return a string if only one segment; always normalize withArray.isArray(slug) ? slug : [slug] - Tab groups reset on tab switch: By default, switching tabs resets the tab's stack; use
backBehavior: "history"in Tabs layout to preserve stack per tab - Android 5-tab limit: Material Design constrains bottom tabs to 5; native tabs enforce this
+not-found.tsxonly catches at its directory level -- a+not-found.tsxinapp/won't catch 404s insideapp/docs/; each directory needs its own if required- Static rendering
generateStaticParamsruns in Node.js -- no access to React Native APIs, browser APIs, or native modules - API route limitation: No dynamic imports, no platform-specific extensions (
+api.web.tsis invalid), bundles to CommonJS - Typed routes are git-ignored -- CI pipelines fail type checks unless types are regenerated with
npx expo customize tsconfig.json - Route files require
export default-- Expo Router discovers screens via default exports; this overrides project "named exports only" conventions for files inapp/
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST define navigation structure in _layout.tsx files -- screens without a layout parent default to a basic Stack)
(You MUST use Stack.Protected with guard prop for authentication flows in SDK 53+ -- NOT imperative redirects in useEffect)
(You MUST use useLocalSearchParams for route params in screens -- useGlobalSearchParams causes unnecessary re-renders on unfocused screens)
(You MUST enable typedRoutes in app.json experiments for compile-time route validation -- catches invalid navigation at build time)
Failure to follow these rules will cause navigation bugs, auth bypasses, unnecessary re-renders, and runtime routing errors that typed routes would catch at compile time.
</critical_reminders>
Frequently asked questions
What to verify before installation and use
What does the mobile-navigation-expo-router source document cover?
Quick Guide: File-based routing for React Native and web. Files in app/ become routes automatically. Use layout.tsx for navigation structure (Stack, Tabs), groups (name)/ for URL-invisible organization, [param] for dynamic segments. SDK 53+: use Stack.Protected with a guard prop…
How do I install mobile-navigation-expo-router?
The source record exposes this install command: npx skills add https://github.com/agents-inc/skills --skill "src/skills/mobile-navigation-expo-router". Inspect the command and pinned source before running it.
Alternatives
Compare before choosing
event4u-app/agent-config
existing-ui-audit
Use BEFORE writing or editing any non-trivial UI — inventories components, design tokens, shadcn primitives, and reusable patterns into state.ui_audit. Hard gate for the ui directive set.
UiPath/skills
uipath-coded-apps
UiPath Coded Apps — scaffold, build, run, and deploy Coded Web Apps and Coded Action Apps: React/TypeScript apps that call UiPath Cloud APIs via the `@uipath/uipath-typescript` SDK and ship to Automation Cloud (push/pull to Studio Web, pack, publish, deploy, OAuth-PKCE). Also generates live analytics & governance dashboards from a plain-language request, wired to tenant data via the Insights real-time API, with edit and deploy flows. For RPA→uipath-rpa, Python agents→uipath-agents, Maestro flows
fcakyon/claude-codex-settings
vercel-react-view-transitions
Guide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions view
yonatangross/orchestkit
json-render-catalog
json-render component catalog patterns for AI-safe generative UI. Define Zod-typed catalogs that constrain what AI can generate, use @json-render/shadcn for 36 pre-built components, optimize specs with YAML mode, and apply the three edit modes (patch/merge/diff) for progressive updates. Use when building AI-generated UIs, defining component catalogs, or integrating json-render into React/Vue/Svelte/React Native/Ink/Next.js projects.