Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Official Expo team skill for building native UI in Expo apps, fine-tuned for Opus models and usable with Claude Code or Cursor.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/route-structure.md
1# Route Structure23## File Conventions45- Routes belong in the `app` directory6- Use `[]` for dynamic routes, e.g. `[id].tsx`7- Routes can never be named `(foo).tsx` - use `(foo)/index.tsx` instead8- Use `(group)` routes to simplify the public URL structure9- NEVER co-locate components, types, or utilities in the app directory - these should be in separate directories like `components/`, `utils/`, etc.10- The app directory should only contain route and `_layout` files; every file should export a default component11- Ensure the app always has a route that matches "/" so the app is never blank12- ALWAYS use `_layout.tsx` files to define stacks1314## Dynamic Routes1516Use square brackets for dynamic segments:1718```19app/20users/21[id].tsx # Matches /users/123, /users/abc22[id]/23posts.tsx # Matches /users/123/posts24```2526### Catch-All Routes2728Use `[...slug]` for catch-all routes:2930```31app/32docs/33[...slug].tsx # Matches /docs/a, /docs/a/b, /docs/a/b/c34```3536## Query Parameters3738Access query parameters with the `useLocalSearchParams` hook:3940```tsx41import { useLocalSearchParams } from "expo-router";4243function Page() {44const { id } = useLocalSearchParams<{ id: string }>();45}46```4748For dynamic routes, the parameter name matches the file name:4950- `[id].tsx` → `useLocalSearchParams<{ id: string }>()`51- `[slug].tsx` → `useLocalSearchParams<{ slug: string }>()`5253## Pathname5455Access the current pathname with the `usePathname` hook:5657```tsx58import { usePathname } from "expo-router";5960function Component() {61const pathname = usePathname(); // e.g. "/users/123"62}63```6465## Group Routes6667Use parentheses for groups that don't affect the URL:6869```70app/71(auth)/72login.tsx # URL: /login73register.tsx # URL: /register74(main)/75index.tsx # URL: /76settings.tsx # URL: /settings77```7879Groups are useful for:8081- Organizing related routes82- Applying different layouts to route groups83- Keeping URLs clean8485## Stacks and Tabs Structure8687When an app has tabs, the header and title should be set in a Stack that is nested INSIDE each tab. This allows tabs to have their own headers and distinct histories. The root layout should often not have a header.8889- Set the 'headerShown' option to false on the tab layout90- Use (group) routes to simplify the public URL structure91- You may need to delete or refactor existing routes to fit this structure9293Example structure:9495```96app/97_layout.tsx — <Tabs />98(home)/99_layout.tsx — <Stack />100index.tsx — <ScrollView />101(settings)/102_layout.tsx — <Stack />103index.tsx — <ScrollView />104(home,settings)/105info.tsx — <ScrollView /> (shared across tabs)106```107108## Array Routes for Multiple Stacks109110Use array routes '(index,settings)' to create multiple stacks. This is useful for tabs that need to share screens across stacks.111112```113app/114_layout.tsx — <Tabs />115(index,settings)/116_layout.tsx — <Stack />117index.tsx — <ScrollView />118settings.tsx — <ScrollView />119```120121This requires a specialized layout with explicit anchor routes:122123```tsx124// app/(index,settings)/_layout.tsx125import { useMemo } from "react";126import Stack from "expo-router/stack";127128export const unstable_settings = {129index: { anchor: "index" },130settings: { anchor: "settings" },131};132133export default function Layout({ segment }: { segment: string }) {134const screen = segment.match(/\((.*)\)/)?.[1]!;135136const options = useMemo(() => {137switch (screen) {138case "index":139return { headerRight: () => <></> };140default:141return {};142}143}, [screen]);144145return (146<Stack>147<Stack.Screen name={screen} options={options} />148</Stack>149);150}151```152153## Complete App Structure Example154155```156app/157_layout.tsx — <NativeTabs />158(index,search)/159_layout.tsx — <Stack />160index.tsx — Main list161search.tsx — Search view162i/[id].tsx — Detail page163components/164theme.tsx165list.tsx166utils/167storage.ts168use-search.ts169```170171## Layout Files172173Every directory can have a `_layout.tsx` file that wraps all routes in that directory:174175```tsx176// app/_layout.tsx177import { Stack } from "expo-router/stack";178179export default function RootLayout() {180return <Stack />;181}182```183184```tsx185// app/(tabs)/_layout.tsx186import { NativeTabs, Icon, Label } from "expo-router/unstable-native-tabs";187188export default function TabLayout() {189return (190<NativeTabs>191<NativeTabs.Trigger name="index">192<Label>Home</Label>193<Icon sf="house.fill" />194</NativeTabs.Trigger>195</NativeTabs>196);197}198```199200## Route Settings201202Export `unstable_settings` to configure route behavior:203204```tsx205export const unstable_settings = {206anchor: "index",207};208```209210- `initialRouteName` was renamed to `anchor` in v4211212## Not Found Routes213214Create a `+not-found.tsx` file to handle unmatched routes:215216```tsx217// app/+not-found.tsx218import { Link } from "expo-router";219import { View, Text } from "react-native";220221export default function NotFound() {222return (223<View>224<Text>Page not found</Text>225<Link href="/">Go home</Link>226</View>227);228}229```230