Architecture
The app follows a modular architecture with clear separation between navigation, UI, state, and data layers.
Directory Structure
app/
├── app/ # Expo Router screens
│ ├── (auth)/ # Auth flow screens
│ ├── (tabs)/ # Main app screens
│ └── _layout.tsx # Root layout with AuthGate
├── components/
│ └── ui/ # Reusable components
├── stores/ # Zustand state
├── hooks/ # Custom hooks
├── lib/ # Utilities
└── constants/ # ConfigurationNavigation
Route Groups
The app uses Expo Router with two route groups:
(auth)- Unauthenticated flow- Stack navigation
- Welcome, signup, onboarding screens
(tabs)- Authenticated app- Bottom tab navigation
- Home, Discover, Create, Clubs, Profile
Auth Gate
The root layout conditionally renders based on auth state:
typescript
// app/_layout.tsx
export default function RootLayout() {
const { isAuthenticated } = useAuthStore()
return (
<Stack>
{isAuthenticated ? (
<Stack.Screen name="(tabs)" />
) : (
<Stack.Screen name="(auth)" />
)}
</Stack>
)
}Layer Responsibilities
Screens (/app)
- Define routes and navigation
- Compose UI components
- Connect to stores and hooks
Components (/components/ui)
- Reusable UI primitives
- Styled with Tamagui
- No business logic
Stores (/stores)
- Global state management
- Zustand with persistence
- Side effects (API calls, storage)
Hooks (/hooks)
- Shared logic
- Theme access
- Feature-specific hooks
Lib (/lib)
- Third-party configurations
- Utility functions
- API client setup
Data Flow
Key Files
| File | Purpose |
|---|---|
app/_layout.tsx | Root layout, providers |
stores/authStore.ts | Auth state & logic |
lib/auth-client.ts | Better Auth config |
tamagui.config.ts | Design system |
constants/config.ts | API URL, feature flags |