askill
expo-router-patterns

expo-router-patternsSafety 85Repository

Expo Router file-based navigation patterns. Use when implementing navigation.

2 stars
1.2k downloads
Updated 2/5/2026

Package Files

Loading files...
SKILL.md

Expo Router Patterns Skill

This skill covers Expo Router navigation for React Native.

When to Use

Use this skill when:

  • Setting up navigation
  • Creating screens and routes
  • Implementing deep linking
  • Protecting routes

Core Principle

FILE-BASED ROUTING - Routes are defined by file structure (like Next.js).

File Structure

app/
├── (auth)/             # Route group (not in URL)
│   ├── login.tsx      # /login
│   ├── register.tsx   # /register
│   └── _layout.tsx    # Layout for auth routes
├── (tabs)/            # Tab navigation group
│   ├── _layout.tsx    # Tabs layout
│   ├── index.tsx      # /
│   └── profile.tsx    # /profile
├── settings/
│   ├── index.tsx      # /settings
│   └── [id].tsx       # /settings/123 (dynamic)
├── _layout.tsx        # Root layout
├── +not-found.tsx     # 404 page
└── [...missing].tsx   # Catch-all route

Basic Navigation

Link Component

import { Link } from 'expo-router';

<Link href="/profile">Go to Profile</Link>

// With params
<Link
  href={{
    pathname: '/user/[id]',
    params: { id: '123' },
  }}
>
  View User
</Link>

// As child (for custom styling)
<Link href="/settings" asChild>
  <TouchableOpacity>
    <Text>Settings</Text>
  </TouchableOpacity>
</Link>

useRouter Hook

import { useRouter } from 'expo-router';

function Component(): React.ReactElement {
  const router = useRouter();

  const handleNavigate = () => {
    // Push new screen
    router.push('/profile');

    // Replace current screen
    router.replace('/login');

    // Go back
    router.back();

    // Navigate with params
    router.push({
      pathname: '/user/[id]',
      params: { id: '123' },
    });
  };

  return <Button onPress={handleNavigate}>Navigate</Button>;
}

Layout Components

Root Layout

// app/_layout.tsx
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';

export default function RootLayout(): React.ReactElement {
  return (
    <>
      <StatusBar style="auto" />
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen name="(auth)" options={{ headerShown: false }} />
        <Stack.Screen
          name="modal"
          options={{
            presentation: 'modal',
            headerTitle: 'Modal',
          }}
        />
      </Stack>
    </>
  );
}

Tab Layout

// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';

export default function TabLayout(): React.ReactElement {
  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: '#3B82F6',
        tabBarInactiveTintColor: '#6B7280',
        headerShown: false,
      }}
    >
      <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>
  );
}

Dynamic Routes

Single Parameter

// app/user/[id].tsx
import { useLocalSearchParams } from 'expo-router';
import { View, Text } from 'react-native';

export default function UserPage(): React.ReactElement {
  const { id } = useLocalSearchParams<{ id: string }>();

  return (
    <View className="flex-1 items-center justify-center">
      <Text className="text-lg">User ID: {id}</Text>
    </View>
  );
}

Multiple Parameters

// app/[category]/[id].tsx
import { useLocalSearchParams } from 'expo-router';

export default function ProductPage(): React.ReactElement {
  const { category, id } = useLocalSearchParams<{
    category: string;
    id: string;
  }>();

  return (
    <View>
      <Text>Category: {category}</Text>
      <Text>Product ID: {id}</Text>
    </View>
  );
}

Catch-All Route

// app/[...path].tsx
import { useLocalSearchParams } from 'expo-router';

export default function CatchAllPage(): React.ReactElement {
  const { path } = useLocalSearchParams<{ path: string[] }>();

  return (
    <View>
      <Text>Path segments: {path?.join('/')}</Text>
    </View>
  );
}

Protected Routes

// app/_layout.tsx
import { Redirect, Stack } from 'expo-router';
import { useAuth } from '@/hooks/useAuth';
import { Loading } from '@/components/Loading';

export default function RootLayout(): React.ReactElement {
  const { user, isLoading } = useAuth();

  if (isLoading) {
    return <Loading />;
  }

  if (!user) {
    return <Redirect href="/login" />;
  }

  return <Stack />;
}

Route Groups

app/
├── (auth)/           # Auth group (no /auth in URL)
│   ├── login.tsx    # /login
│   └── register.tsx # /register
├── (app)/           # App group (no /app in URL)
│   ├── home.tsx     # /home
│   └── profile.tsx  # /profile

Modal Routes

// app/_layout.tsx
<Stack>
  <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
  <Stack.Screen
    name="modal"
    options={{
      presentation: 'modal',
      animation: 'slide_from_bottom',
    }}
  />
</Stack>

// Navigate to modal
router.push('/modal');

// Close modal
router.back();

Deep Linking

Configuration

// app.json
{
  "expo": {
    "scheme": "myapp",
    "web": {
      "bundler": "metro"
    }
  }
}

Links

myapp://                    # Opens app
myapp://profile             # Opens /profile
myapp://user/123            # Opens /user/123
https://myapp.com/profile   # Universal link

Navigation Hooks

usePathname

import { usePathname } from 'expo-router';

function Component(): React.ReactElement {
  const pathname = usePathname();
  // Returns: "/user/123"

  return <Text>Current path: {pathname}</Text>;
}

useSegments

import { useSegments } from 'expo-router';

function Component(): React.ReactElement {
  const segments = useSegments();
  // Returns: ["user", "123"]

  return <Text>Segments: {segments.join(', ')}</Text>;
}

useFocusEffect

import { useFocusEffect } from 'expo-router';
import { useCallback } from 'react';

function Screen(): React.ReactElement {
  useFocusEffect(
    useCallback(() => {
      // Runs when screen is focused
      console.log('Screen focused');

      return () => {
        // Cleanup when screen loses focus
        console.log('Screen unfocused');
      };
    }, [])
  );

  return <View />;
}

Notes

  • Use route groups (name) to organize without affecting URLs
  • Layouts cascade (parent layouts wrap children)
  • _layout.tsx defines the navigation structure
  • +not-found.tsx handles 404 routes
  • Deep linking is configured automatically
  • Use typed params with generics for type safety

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

91/100Analyzed 2/19/2026

High-quality technical reference skill covering Expo Router file-based navigation comprehensively. Includes clear When to Use section, structured code examples for all major patterns (Link, useRouter, layouts, dynamic routes, protected routes, route groups, modals, deep linking, hooks), and well-organized documentation. The 'security' tag is misplaced but doesn't significantly impact quality. Located in dedicated skills folder with good metadata. Score slightly reduced due to the irrelevant tag.

85
90
90
90
95

Metadata

Licenseunknown
Version-
Updated2/5/2026
PublisherIvanTorresEdge

Tags

security