Skip to content

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/            # Configuration

Route Groups

The app uses Expo Router with two route groups:

  1. (auth) - Unauthenticated flow

    • Stack navigation
    • Welcome, signup, onboarding screens
  2. (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

FilePurpose
app/_layout.tsxRoot layout, providers
stores/authStore.tsAuth state & logic
lib/auth-client.tsBetter Auth config
tamagui.config.tsDesign system
constants/config.tsAPI URL, feature flags

Built with VitePress