Project Structure
RNCopilot follows a clear separation between routing (the app/ directory, managed by Expo Router) and application logic (the src/ directory). This page walks through both layers and explains the conventions used throughout the codebase.
Top-Level Overview
app/ # Expo Router (file-based routing)
├── _layout.tsx # Root layout (providers, error boundary)
├── +not-found.tsx # 404 screen
└── (main)/(tabs)/ # Tab navigation
├── _layout.tsx # Tab bar configuration
├── index.tsx # Home tab
└── settings.tsx # Settings screen
src/
├── common/components/ # 33 shared UI components
├── config/ # Environment configuration
├── features/ # Feature modules
├── hooks/ # Global hooks
├── i18n/ # Translations (en.json, ar.json)
├── integrations/ # Supabase client
├── providers/ # Query provider, auth store
├── services/api/ # Axios client
├── theme/ # Colors, metrics, fonts
├── types/ # Global TypeScript types
└── utils/storage/ # MMKV utilitiesThe app/ Directory
Expo Router uses file-based routing, meaning every file in app/ becomes a route. RNCopilot keeps this layer thin — route files are responsible only for composing screen components, not for business logic.
Key Files
| File | Purpose |
|---|---|
_layout.tsx | Root layout that wraps the entire app with providers (QueryProvider, theme, i18n), the ErrorBoundary, and auth initialization. |
+not-found.tsx | Catch-all 404 screen for unmatched routes. |
(main)/(tabs)/_layout.tsx | Configures the bottom tab navigator with themed tab bar. |
(main)/(tabs)/index.tsx | Home tab screen. |
(main)/(tabs)/settings.tsx | Settings screen with theme and language controls. |
Route groups like (main) and (tabs) use parentheses in their names. These create layout nesting without adding URL segments — (main)/(tabs)/index.tsx maps to the / route, not /main/tabs/.
The src/ Directory
All application logic lives under src/, organized into clear domains.
common/components/
The shared component library with 33 components, organized by category:
| Category | Components |
|---|---|
| Action | Button, IconButton |
| Data Display | Text, Avatar, Badge, Card, Chip, ListItem |
| Disclosure | Accordion |
| Feedback | EmptyState, ErrorBoundary, Loading, ProgressBar, Skeleton, Snackbar |
| Form | Checkbox, FormField, Input, RadioGroup, SearchBar, SegmentedControl, Select, Switch, TextArea |
| Layout | Divider, ScreenContainer |
| Overlay | Dialog, Menu |
| Typography | Icon, Text |
config/
Centralized environment configuration with validation. All environment variables are read and validated here, providing typed access throughout the app.
features/
Feature modules that encapsulate domain-specific logic. Each feature is self-contained with its own components, services, hooks, stores, types, and schemas.
hooks/
Global hooks used across multiple features:
useBottomPadding— Safe area bottom paddinguseNetworkStatus— Online/offline detectionuseScreenDimensions— Responsive screen measurementsuseProtectedRoute— Auth-gated navigation
i18n/
Internationalization configuration and translation files. Ships with English (en.json) and Arabic (ar.json) with full RTL support.
integrations/
Third-party service clients. Currently contains the Supabase client, which initializes gracefully even when credentials are not configured.
providers/
React providers and Zustand stores:
- QueryProvider — Configures @tanstack/react-query with MMKV persistence
- Auth store — Zustand-based authentication state management
services/api/
Axios HTTP client with interceptors for authentication token injection and standardized error handling.
theme/
The complete theme system:
- Colors — Indigo-based palette with Teal accent, semantic tokens for light/dark modes
- Metrics — Responsive scaling functions (
rf,hs,vs) - Fonts — Font family and weight definitions
types/
Global TypeScript type definitions shared across the application.
utils/storage/
MMKV storage utilities with typed keys for type-safe persistent storage.
Component Pattern
Every shared component follows a consistent file structure:
ComponentName/
├── ComponentName.tsx # Component implementation
├── ComponentName.styles.ts # Styles (optional)
├── ComponentName.types.ts # TypeScript types (optional)
└── index.ts # Public exportThe index.ts barrel file re-exports the component and its types, providing a clean public API:
// Importing a component
import { Button } from '@/common/components/Button';Why this pattern matters:
- Colocation — Styles, types, and implementation live together.
- Encapsulation — Internal details stay private; only what
index.tsexports is public. - Discoverability — Consistent structure means you always know where to find things.
Style files use StyleSheet.create from react-native-unistyles, not the standard React Native StyleSheet. This gives you access to theme tokens directly in your style definitions.
Feature Module Pattern
Feature modules follow a similar philosophy of colocation but at a larger scale:
feature-name/
├── components/ # Feature-specific UI components
├── services/ # API calls and data fetching
├── hooks/ # Feature-specific hooks
├── stores/ # Zustand stores
├── types/ # TypeScript types
├── schemas/ # Zod validation schemas
└── constants/ # Feature constantsNot every feature needs all of these directories — create only what you need. The key principle is that feature-specific code stays inside the feature module, while truly shared code goes into src/common/, src/hooks/, or src/services/.
When to Create a Feature vs. Use Common
| Scenario | Where It Goes |
|---|---|
| A Button component used on 10 screens | src/common/components/Button/ |
| A ProductCard used only in the shop feature | src/features/shop/components/ProductCard/ |
| An API hook for fetching user profiles | src/features/auth/hooks/useProfile.ts |
A generic useDebounce hook | src/hooks/useDebounce.ts |
Path Aliases
Two path aliases eliminate long relative imports:
| Alias | Resolves To | Usage |
|---|---|---|
@/* | src/* | import { Button } from '@/common/components/Button' |
~/* | app/* | import type { LayoutProps } from '~/types' |
These are configured in both tsconfig.json and babel.config.js.