- Framework: React Native
- Language: TypeScript
- State Management: React Native Onyx
- Navigation: React Navigation
- Platforms: iOS, Android, Web
IMPORTANT: The mobile application is built from the Mobile-Expensify submodule, not directly from the App repository. IMPORTANT: NewDot refers to the new Expensify App, OldDot or Expensify Classic refers to our Old expensify app and website
- App (NewDot) and Mobile-Expensify (OldDot) are combined into a single mobile application
- The HybridApp module (
@expensify/react-native-hybrid-app) manages transitions between OldDot and NewDot - Build process merges dotenv configurations from both repositories
- Environment variables from Mobile-Expensify take precedence over App variables
- Mobile builds must be initiated from the Mobile-Expensify directory
- Standalone: Pure NewDot application (web)
- HybridApp: Combined OldDot + NewDot (mobile apps)
- Controlled via
STANDALONE_NEW_DOTenvironment variable
src/App.tsx: Main application component with provider hierarchysrc/Expensify.tsx: Core application logic and initializationsrc/HybridAppHandler.tsx: Manages HybridApp transitions and authenticationindex.js: React Native entry point
The application uses a nested provider structure for context management:
- SplashScreenStateContextProvider: Manages splash screen visibility
- InitialURLContextProvider: Handles deep linking
- ThemeProvider: Theme management
- LocaleContextProvider: Internationalization
- OnyxListItemProvider: Data layer provider
- SafeAreaProvider: Device safe areas
- PopoverContextProvider: Global popover state
- KeyboardProvider: Keyboard state management
- Onyx: Custom data persistence layer for offline-first functionality
- ONYXKEYS.ts: Centralized key definitions for data store
- Supports optimistic updates and conflict resolution
-
Expense Management
- Receipt scanning and SmartScan
- Expense creation and editing
- Distance tracking
- Per diem support
- Split expenses
-
Reporting
- Report creation and submission
- Approval workflows
- Report fields and custom attributes
- Bulk operations
-
Workspace/Policy Management
- Policy creation and configuration
- Member management
- Categories, tags, and tax rates
- Accounting integration settings
- Approval workflows
-
Travel
- Trip management
- Travel booking integration
- Travel policy enforcement
-
Search & Filtering
- Advanced search with filters
- Saved searches
- Search parser (Peggy-based)
-
Payment & Cards
- Expensify Card management
- Bank account connections (Plaid)
- Payment methods
- Company cards integration
- Wallet functionality
-
Accounting Integrations
- QuickBooks Online
- Xero
- NetSuite
- Sage Intacct
- QuickBooks Desktop
- Generic accounting connections
-
Communication
- Chat functionality
- Task management
- Mentions and notifications
- Thread organization
-
Invoice Management
- Invoice creation and sending
- Invoice rooms
src/SCREENS.ts: Screen name constantssrc/ROUTES.ts: Route definitions and builderssrc/NAVIGATORS.ts: Navigator configuration
- ProtectedScreens: Authenticated app screens
- PublicScreens: Login and onboarding screens
- RHP (Right Hand Panel/Pane): Settings and details panel
- Central Pane: Main content area
- LHN (Left Hand Navigation): Report list and navigation
- RHP: Contextual panels and settings
- Session: Authentication and user session
- Personal Details: User profiles and preferences
- Reports: Chat and expense reports
- Transactions: Expense transactions
- Policy: Workspace configuration
- Forms: Form state management
Major action categories:
App.ts: Application lifecycleIOU.ts: Money requests and expensesReport.ts: Report managementPolicy/: Workspace operationsUser.ts: User account operationsSession.ts: AuthenticationSearch.ts: Search operationsTravel.ts: Travel features
Key GitHub Actions workflows:
deploy.yml: Production deploymentpreDeploy.yml: Staging deploymenttestBuild.yml: PR test buildstest.yml: Unit teststypecheck.yml: TypeScript validationlint.yml: Code quality checks
- Path:
App/Mobile-Expensify/ - Purpose: Legacy OldDot application and mobile build source
- Critical: All mobile builds originate from this directory
- Contains platform-specific code for iOS and Android
- Manages the HybridApp integration layer
- Purpose: Shared libraries and utilities
- Contains common validation, parsing, and utility functions
- Used across multiple Expensify repositories
Use the /react-native-best-practices skill when working on performance-sensitive code, native modules, or release preparation. This ensures code respects established best practices from the start, resulting in more consistent code, fewer review iterations, and better resilience against regressions.
The skill provides guidance on:
- Performance: FPS optimization, virtualized lists (FlashList), memoization, atomic state, animations
- Bundle & App Size: Barrel imports, tree shaking, bundle analysis, R8 shrinking
- Startup (TTI): Hermes bytecode optimization, native navigation, deferred work
- Native Modules: Turbo Module development, threading model, Swift/Kotlin/C++ patterns
- Memory: JS and native memory leak detection and patterns
- Build Compliance: Android 16KB page alignment (Google Play requirement)
- Platform Tooling: Xcode/Android Studio profiling and debugging setup
- TypeScript: Strict mode enabled
- ESLint: Linter
- Prettier: Code formatting - run
npm run prettierafter making changes - Patch Management: patch-package for dependency fixes
ALWAYS run these steps after making code changes, before committing:
- Prettier: Run
npx prettier --write <changed files>on every file you modified. This is mandatory - CI will reject unformatted code. - ESLint: Run
npx eslint <changed files> --max-warnings=0to catch lint errors early. - TypeScript: Run
npm run typecheck-tsgoafter changes that may affect typing (types, interfaces, or function signatures). It is ~10x faster and usually stricter than tsc. CI validates withnpm run typecheck(tsc), which remains the required merge gate.
- Unit Tests: Jest with React Native Testing Library
- Performance Tests: Reassure framework
- All features work offline
- Optimistic updates with rollback
- Queue-based request handling
- Conflict resolution strategies
- Push notifications via Airship
- Mapbox integration for location features
- Camera and gallery access
- Content Security Policy enforcement
- Two-factor authentication support
- NewDot Help: https://help.expensify.com/new-expensify/hubs/
- OldDot/Expensify Classic Help: https://help.expensify.com/expensify-classic/hubs/
# Install dependencies
npm install
# Clean build artifacts
npm run clean
# Type checking (tsgo, fast, for development only)
npm run typecheck-tsgo
# Type checking (tsc, CI production gate)
npm run typecheck
# Linting
npm run lint
# Format code with Prettier
npm run prettier
# Testing
npm run test# iOS build
npm run ios
# Android build
npm run android
# Web build
npm run web- Location: Runs on HOST machine (not in VM)
- URL:
https://dev.new.expensify.com:8082/ - Start command:
npm run web - VM is only for: Backend services (Auth, Bedrock, Integration-Server, Web-Expensify)
Use the /playwright-app-testing skill to test and debug the App in a browser. Use this skill after making frontend changes to verify your work, or when the user requests testing.
- Fabric renderer enabled
- TurboModules for native module integration
- Hermes JavaScript engine
- Custom Onyx library for offline-first capabilities
- Optimistic updates as default pattern
- Centralized action layer for API calls
- Direct key-value storage with automatic persistence
- React Navigation for cross-platform consistency
- Custom navigation state management
- Deep linking support
- Session sharing via HybridApp module
- Navigation handoff between apps
- Shared authentication state
- Environment variable merging
- RESTful API communication
- WebSocket connections via Pusher
- Real-time synchronization