Navigation
TrickBook uses Expo Router for file-based navigation, similar to Next.js. Routes are defined by the file system structure in the app/ directory.
Navigation Architecture
app/
│
├── _layout.tsx # Root layout (providers + auth gate)
│ │
│ ├── IF user authenticated:
│ │ └── (tabs)/_layout.tsx (Bottom Tab Navigator)
│ │
│ └── ELSE (not authenticated):
│ └── (auth)/_layout.tsx (Stack Navigator)
│
├── (auth)/ # Auth group
│ ├── welcome.tsx
│ ├── login.tsx
│ └── register.tsx
│
├── (tabs)/ # Main tab group
│ ├── index.tsx # Home tab
│ ├── trickbook/ # TrickBook tab
│ ├── spots/ # Spots tab
│ ├── homies/ # Homies tab
│ ├── media/ # Media tab
│ └── profile/ # Profile (hidden tab)
│
└── profile/ # Profile settings (stack)
├── edit.tsx
├── settings.tsx
└── ...
Root Layout
The root layout sets up providers and conditionally renders auth or main app screens.
// app/_layout.tsx
import { Stack } from 'expo-router';
import { QueryClientProvider } from '@tanstack/react-query';
import { ThemeProvider } from '@/lib/providers/ThemeProvider';
import { useAuthStore } from '@/lib/stores/authStore';
export default function RootLayout() {
const { user, isLoading, loadStoredAuth } = useAuthStore();
useEffect(() => {
loadStoredAuth();
}, []);
if (isLoading) return <SplashScreen />;
return (
<SafeAreaProvider>
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<Stack screenOptions={{ headerShown: false }}>
{user ? (
<Stack.Screen name="(tabs)" />
) : (
<Stack.Screen name="(auth)" />
)}
</Stack>
</ThemeProvider>
</QueryClientProvider>
</SafeAreaProvider>
);
}
Auth Group
Handles authentication screens before user login.
// app/(auth)/_layout.tsx
import { Stack } from 'expo-router';
export default function AuthLayout() {
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="welcome" />
<Stack.Screen name="login" />
<Stack.Screen name="register" />
</Stack>
);
}
Screens:
| Screen | File | Purpose |
|---|---|---|
| Welcome | welcome.tsx | Landing page with login/register options |
| Login | login.tsx | Email/password + Google SSO + Apple Sign-In |
| Register | register.tsx | New user registration |
Tab Navigator
Main navigation for authenticated users with 5 tabs.
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
export default function TabLayout() {
return (
<Tabs>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color, size }) => (
<Ionicons name="home" color={color} size={size} />
),
}}
/>
<Tabs.Screen
name="trickbook"
options={{
title: 'TrickBook',
tabBarIcon: ({ color, size }) => (
<Ionicons name="list" color={color} size={size} />
),
}}
/>
<Tabs.Screen
name="spots"
options={{
title: 'Spots',
tabBarIcon: ({ color, size }) => (
<Ionicons name="location" color={color} size={size} />
),
}}
/>
<Tabs.Screen
name="homies"
options={{
title: 'Homies',
tabBarIcon: ({ color, size }) => (
<Ionicons name="people" color={color} size={size} />
),
}}
/>
<Tabs.Screen
name="media"
options={{
title: 'Media',
tabBarIcon: ({ color, size }) => (
<Ionicons name="play-circle" color={color} size={size} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
href: null, // Hidden from tab bar
}}
/>
</Tabs>
);
}
Tabs:
| Tab | Directory | Purpose |
|---|---|---|
| Home | index.tsx | Dashboard with stats, goals, activity |
| TrickBook | trickbook/ | Trick list management and Trickipedia |
| Spots | spots/ | Spot discovery, maps, reviews |
| Homies | homies/ | Friend network and profiles |
| Media | media/ | Social feed and The Couch |
| Profile | profile/ | User profile (hidden from tab bar, accessed via Home) |
Profile Routes
Stack navigation for profile and settings screens.
app/profile/
├── index.tsx # User profile view
├── edit.tsx # Edit profile info
├── settings.tsx # App settings
├── account.tsx # Account management
├── change-password.tsx # Password update
├── notifications.tsx # Notification preferences
├── privacy.tsx # Privacy settings
├── theme.tsx # Dark/Light theme selection
└── support.tsx # Help and support
Navigation Patterns
Programmatic Navigation
import { useRouter } from 'expo-router';
const MyComponent = () => {
const router = useRouter();
// Navigate to a route
router.push('/spots/details/123');
// Navigate back
router.back();
// Replace current screen
router.replace('/(auth)/login');
};
Link Component
import { Link } from 'expo-router';
<Link href="/trickbook/list/123">
View Trick List
</Link>
Dynamic Routes
// app/(tabs)/spots/[id].tsx
import { useLocalSearchParams } from 'expo-router';
export default function SpotDetails() {
const { id } = useLocalSearchParams<{ id: string }>();
// Fetch spot data using id...
}
Navigation Flow Examples
Login Flow
(auth)/welcome.tsx
│
├── "Login" button
│ │
│ ▼
│ (auth)/login.tsx
│ │
│ ├── Email/Password → POST /api/auth
│ ├── Google SSO → OAuth2 flow
│ └── Apple Sign-In → Apple auth flow
│ │
│ ├── Success → authStore.login()
│ │ → Root layout redirects to (tabs)
│ │
│ └── Back → welcome.tsx
│
└── "Register" button
│
▼
(auth)/register.tsx
Trick List Flow
(tabs)/trickbook/index.tsx (list of all trick lists)
│
├── Tap list card
│ │
│ ▼
│ (tabs)/trickbook/[listId].tsx (tricks in list)
│ │
│ ├── Tap "+" → Add trick modal
│ │
│ ├── Tap trick → Trick detail view
│ │
│ └── Swipe trick → Edit/Delete actions
│
└── Tap "+" header button
│
▼
Create new trick list
Spots Flow
(tabs)/spots/index.tsx (map view + spot list)
│
├── Tap spot marker on map
│ │
│ ▼
│ Spot detail view
│ │
│ ├── View reviews
│ ├── Add review (StarRatingInput)
│ ├── Add to spot list
│ └── Get directions
│
└── "My Lists" button
│
▼
Spot list management
Key Differences from React Navigation
| Feature | Old (React Navigation 6) | New (Expo Router) |
|---|---|---|
| Route definition | <Stack.Screen component={...}> | File-based (app/page.tsx) |
| Navigation | navigation.navigate('Screen') | router.push('/path') |
| Params | route.params.id | useLocalSearchParams() |
| Deep linking | Manual config | Automatic from file structure |
| Type safety | Manual types | Generated from file paths |
| Layouts | Inline in navigators | _layout.tsx files |
| Groups | Not supported | (groupName)/ directories |