Expo Router Skill
Route scaffolding, typed params, and deep-link wiring - an Agent Skill for adding screens safely on Expo Router with Expo SDK 57 file-based routing.
Search across all documentation pages
Route scaffolding, typed params, and deep-link wiring - an Agent Skill for adding screens safely on Expo Router with Expo SDK 57 file-based routing.
Generates:
app/ route file tree (layouts, index, dynamic segments)experiments.typedRoutes config and href builder modulesapp.config.tstsc --noEmit verification stepsapp/ (tabs, stacks, modals)/orders/[id] with typed navigationmyapp:// or https:// deep links to existing screensrouter.push calls to centralized Href builders| Input | Why |
|---|---|
| Route map | Paths, groups (auth), tab vs stack |
| Param shapes | { id: string }, optional query fields |
app.config.ts | scheme, ios.bundleIdentifier, linking |
Existing app/ tree | Avoid duplicate layouts |
| Deep link examples | myapp://orders/42, notification payloads |
app/...)app.config.ts linking snippetsrc/navigation/hrefs.ts (or routes.ts) with typed buildersnpx expo start, npx tsc --noEmitexperiments.typedRoutes: true - regenerate types after every app/ change (Typed Routes).href - { pathname: "/orders/[id]", params: { id } }.src/features/ (Mobile Architecture Basics)._layout providers - one auth/session boundary at root.Quick-reference recipe card - copy-paste ready.
// app.config.ts
export default {
scheme: "myapp",
experiments: { typedRoutes: true },
} satisfies import("expo/config").ExpoConfig;// app/orders/[id].tsx
import { useLocalSearchParams } from "expo-router";
import { z } from "zod";
const paramsSchema = z.object({ id: z.string().min(1) });
export default function OrderDetailScreen() {
const raw = useLocalSearchParams<{ id: string }>();
const { id } = paramsSchema.parse(raw);
// ...
}// src/navigation/hrefs.ts
import type { Href } from "expo-router";
export const orderDetailHref = (id: string): Href => ({
pathname: "/orders/[id]",
params: { id },
});// Usage
import { Link, router } from "expo-router";
import { orderDetailHref } from "@/navigation/hrefs";
<Link href={orderDetailHref("42")}>Order 42</Link>
router.push(orderDetailHref("42"));npx expo start # regen .expo/types/router.d.ts
npx tsc --noEmit # catch invalid href at compile timeWhen to reach for this skill:
app/ restructureapp/
├── _layout.tsx # root Stack or Slot
├── (tabs)/
│ ├── _layout.tsx
│ ├── index.tsx
│ └── orders/
│ ├── index.tsx
│ └── [id].tsx
└── (auth)/
├── _layout.tsx
└── login.tsx(tabs) don't appear in URL - Route Groups// app/_layout.tsx
import { Stack, useRouter, useSegments } from "expo-router";
import { useEffect } from "react";
import { useSession } from "@/features/auth/session";
export default function RootLayout() {
const { session, isLoading } = useSession();
const segments = useSegments();
const router = useRouter();
useEffect(() => {
if (isLoading) return;
const inAuth = segments[0] === "(auth)";
if (!session && !inAuth) router.replace("/(auth)/login");
if (session && inAuth) router.replace("/(tabs)");
}, [session, isLoading, segments]);
return <Stack screenOptions={{ headerShown: false }} />;
}// app.config.ts
export default {
scheme: "myapp",
ios: { bundleIdentifier: "com.example.myapp" },
android: { package: "com.example.myapp" },
// Optional universal links:
// ios: { associatedDomains: ["applinks:myapp.com"] },
} satisfies import("expo/config").ExpoConfig;| URL | Resolves to |
|---|---|
myapp://orders/42 | app/orders/[id].tsx with id=42 |
myapp://(tabs)/orders | orders list |
router.replace("/(tabs)") post-loginnpx expo start
npx tsc --noEmit
# iOS simulator deep link test
xcrun simctl openurl booted "myapp://orders/42"Use Expo Router skill. SDK 57, typedRoutes on.
Add /settings/notifications and /settings/profile under (tabs).
Provide href builders and tsc verification. Routes thin - screens live in src/features/settings/.Use Expo Router skill.
Deep link: myapp://orders/[id] from push payload { orderId }.
Map to typed route, Zod validate id, document simctl test command.Use Expo Router skill.
Migrate router.push("/order/" + id) calls to centralized Href module.
List every file to touch and tsc gate.- [ ] app/ tree documented
- [ ] experiments.typedRoutes: true in app.config.ts
- [ ] href builders exported from single module
- [ ] Zod (or equivalent) on dynamic params
- [ ] scheme / associatedDomains if deep links required
- [ ] npx tsc --noEmit in verification section
- [ ] No business logic duplicated in route filesYes when requested - use presentation: "modal" in layout options. See Modal Routes for SDK 57 presentation patterns.
Partially. Generated Href unions cover pathnames; optional search params still need runtime validation at screen entry.
Skill should use expo-router Href type - works on web and native. Platform-specific linking config belongs in app.config.ts only.
Href unionsStack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026