Skip to content

Project Structure

If you open the new project in VSCode you will see the following structure:

  • Directoryfeatures ## feature-oriented modules
    • Directoryauth/
      • login-screen.tsx
      • use-auth-store.tsx
      • Directorycomponents/
        • login-form.tsx
        • login-form.test.tsx
    • Directoryfeed/
      • feed-screen.tsx
      • post-detail-screen.tsx
      • add-post-screen.tsx
      • api.ts
      • Directorycomponents/
        • post-card.tsx
    • Directorysettings/
      • settings-screen.tsx
      • Directorycomponents/
        • settings-item.tsx
        • settings-container.tsx
        • language-item.tsx
        • theme-item.tsx
    • Directoryonboarding/
      • onboarding-screen.tsx
      • Directorycomponents/
        • cover.tsx
    • Directorystyle-demo/
      • style-screen.tsx
      • Directorycomponents/
        • typography-demo.tsx
        • colors-demo.tsx
        • buttons-demo.tsx
        • inputs-demo.tsx
        • title.tsx
  • Directoryapp ## app routes and layouts (Expo Router)
    • Directory(app)/
      • _layout.tsx
      • index.tsx (re-exports FeedScreen)
      • settings.tsx (re-exports SettingsScreen)
      • style.tsx (re-exports StyleScreen)
    • Directoryfeed/
      • [id].tsx (re-exports PostDetailScreen)
      • add-post.tsx (re-exports AddPostScreen)
    • _layout.tsx
    • login.tsx (re-exports LoginScreen)
    • onboarding.tsx (re-exports OnboardingScreen)
  • Directorycomponents ## design system and shared UI components
    • Directoryui/
      • button.tsx
      • checkbox.tsx
      • colors.js
      • focus-aware-status-bar.tsx
      • form-utils.ts
      • Directoryicons/
      • image.tsx
      • input.tsx
      • list.tsx
      • modal.tsx
      • progress-bar.tsx
      • select.tsx
      • text.tsx
      • use-theme-config.tsx
      • utils.tsx
  • Directorylib ## core infrastructure and utilities
    • Directoryapi/
      • index.ts
      • client.tsx
      • provider.tsx
      • utils.tsx (includes PaginateQuery type)
    • Directoryauth/
      • utils.tsx (token storage utilities)
    • Directoryhooks/
      • use-is-first-time.tsx
      • use-selected-theme.tsx
    • Directoryi18n/
      • index.tsx
      • resources.ts
      • types.ts
      • utils.tsx
    • storage.tsx
    • test-utils.tsx
    • utils.ts
  • Directorytranslations ## translation resources files
    • ar.json
    • en.json

The project follows a feature-oriented architecture where each feature is self-contained with its screens, components, API calls, and state management. This approach promotes:

  • Feature isolation: Each feature has all related code in one place
  • Better scalability: Easy to add new features without affecting existing ones
  • Improved maintainability: Clear boundaries between features
  • Team collaboration: Multiple developers can work on different features simultaneously

This is where all feature-specific code lives. Each feature folder follows a consistent pattern:

  • Screens: Named with -screen.tsx suffix, placed in the feature root
  • components/: Feature-specific components that are only used within this feature
  • api.ts: Feature-specific API calls and React Query hooks (optional)
  • use-*-store.tsx: Feature-specific state management with Zustand (optional)

Example: The auth feature contains the login screen, login form component, and auth store all in one place.

Convention:

  • ✅ Screens use -screen.tsx suffix
  • ✅ Only components/ subfolder is allowed
  • ✅ Use single files (api.ts) not folders
  • ❌ No index.ts barrel exports (prevents fast refresh issues)

This folder contains the Expo Router file-based routing structure. Routes are thin re-export layers that import screens from features:

app/login.tsx
export { LoginScreen as default } from '@/features/auth/login-screen';

This keeps routing configuration separate from feature logic while maintaining Expo Router’s file-based routing benefits.

This is the design system containing reusable UI primitives and shared utilities:

  • UI components (Button, Input, Text, etc.)
  • Form utilities (form-utils.ts)
  • Theme configuration (use-theme-config.tsx)
  • Icon components

When to promote a component here: When it’s used in 2+ features and has no feature-specific logic.

Core infrastructure and cross-cutting concerns:

  • api/: Shared API client, React Query provider, and utilities
    • Includes PaginateQuery type used across features
    • Axios client configuration
    • API helper functions
  • auth/: Low-level token storage utilities (getToken, setToken, removeToken)
  • hooks/: Global hooks (useIsFirstTime, useSelectedTheme)
  • i18n/: Internationalization setup and utilities
  • Core utilities: Storage abstraction, environment config, test utilities

What stays here: Infrastructure that features depend on but don’t own.

Contains i18n resource files. We recommend using translation files even if you only support one language initially, as it:

  • Makes future multi-language support easier
  • Centralizes all user-facing strings
  • Facilitates copy changes without code modifications

We use absolute imports with direct file paths (no barrel exports) to avoid fast refresh issues:

// ✅ CORRECT - Direct imports
import { Button, Input, Text } from '@/components/ui';
import { LoginScreen } from '@/features/auth/login-screen';
import { usePosts } from '@/features/feed/api';
import { translate } from '@/lib/i18n';
import { client } from '@/lib/api';
// ❌ WRONG - Barrel exports (causes fast refresh issues)
import { LoginScreen } from '@/features/auth';
import { usePosts } from '@/features/feed';

Within the same feature (use relative imports):

import { LoginForm } from './components/login-form';
import { useAuthStore } from './use-auth-store';

Cross-feature imports (use absolute with full path):

import { useAuthStore } from '@/features/auth/use-auth-store';
import { PostCard } from '@/features/feed/components/post-card';

From design system:

import { Button, Input, View } from '@/components/ui';
import { getFieldError } from '@/components/ui/form-utils';
import { useThemeConfig } from '@/components/ui/use-theme-config';

From lib:

import { translate } from '@/lib/i18n';
import { useIsFirstTime } from '@/lib/hooks/use-is-first-time';
import { client, APIProvider } from '@/lib/api';

This absolute import pattern is enabled through Babel module resolver and TypeScript path mapping:

⚙️ Babel configuration
babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: [
'babel-preset-expo',
],
plugins: [
[
'module-resolver',
{
root: ['./'],
alias: {
'@': './src',
'@env': './src/lib/env.js',
},
extensions: [
'.ios.ts',
'.android.ts',
'.ts',
'.ios.tsx',
'.android.tsx',
'.tsx',
'.jsx',
'.js',
'.json',
],
},
],
'react-native-reanimated/plugin',
],
};
};
⚙️ Typescript Config
tsconfig.json
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@env": ["env.ts"]
},
"checkJs": true,
"strict": true,
"esModuleInterop": true
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts",
"nativewind-env.d.ts",
"env.ts"
],
"exclude": [
"node_modules",
"babel.config.js",
"metro.config.js",
"docs",
"cli",
"android",
"ios",
"lint-staged.config.js"
]
}

To add a new feature, follow this template:

Terminal window
# Create feature structure
mkdir -p src/features/my-feature/components
# Create screen
touch src/features/my-feature/my-feature-screen.tsx
# Create route re-export
echo "export { MyFeatureScreen as default } from '@/features/my-feature/my-feature-screen';" > src/app/my-feature.tsx

For features with API:

Terminal window
mkdir -p src/features/my-feature/components
touch src/features/my-feature/my-feature-screen.tsx
touch src/features/my-feature/api.ts
touch src/features/my-feature/components/my-component.tsx