Skip to Content
Getting StartedProject Structure

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 utilities

The 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

FilePurpose
_layout.tsxRoot layout that wraps the entire app with providers (QueryProvider, theme, i18n), the ErrorBoundary, and auth initialization.
+not-found.tsxCatch-all 404 screen for unmatched routes.
(main)/(tabs)/_layout.tsxConfigures the bottom tab navigator with themed tab bar.
(main)/(tabs)/index.tsxHome tab screen.
(main)/(tabs)/settings.tsxSettings 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:

CategoryComponents
ActionButton, IconButton
Data DisplayText, Avatar, Badge, Card, Chip, ListItem
DisclosureAccordion
FeedbackEmptyState, ErrorBoundary, Loading, ProgressBar, Skeleton, Snackbar
FormCheckbox, FormField, Input, RadioGroup, SearchBar, SegmentedControl, Select, Switch, TextArea
LayoutDivider, ScreenContainer
OverlayDialog, Menu
TypographyIcon, 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 padding
  • useNetworkStatus — Online/offline detection
  • useScreenDimensions — Responsive screen measurements
  • useProtectedRoute — 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 export

The 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.ts exports 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 constants

Not 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

ScenarioWhere It Goes
A Button component used on 10 screenssrc/common/components/Button/
A ProductCard used only in the shop featuresrc/features/shop/components/ProductCard/
An API hook for fetching user profilessrc/features/auth/hooks/useProfile.ts
A generic useDebounce hooksrc/hooks/useDebounce.ts

Path Aliases

Two path aliases eliminate long relative imports:

AliasResolves ToUsage
@/*src/*import { Button } from '@/common/components/Button'
~/*app/*import type { LayoutProps } from '~/types'

These are configured in both tsconfig.json and babel.config.js.

Last updated on