Skip to main content

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.

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:

ScreenFilePurpose
Welcomewelcome.tsxLanding page with login/register options
Loginlogin.tsxEmail/password + Google SSO + Apple Sign-In
Registerregister.tsxNew 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:

TabDirectoryPurpose
Homeindex.tsxDashboard with stats, goals, activity
TrickBooktrickbook/Trick list management and Trickipedia
Spotsspots/Spot discovery, maps, reviews
Homieshomies/Friend network and profiles
Mediamedia/Social feed and The Couch
Profileprofile/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

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');
};
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...
}

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

FeatureOld (React Navigation 6)New (Expo Router)
Route definition<Stack.Screen component={...}>File-based (app/page.tsx)
Navigationnavigation.navigate('Screen')router.push('/path')
Paramsroute.params.iduseLocalSearchParams()
Deep linkingManual configAutomatic from file structure
Type safetyManual typesGenerated from file paths
LayoutsInline in navigators_layout.tsx files
GroupsNot supported(groupName)/ directories