Mobile Development
Expo setup, simulators, and code sync from web
The Vezta mobile app is built with Expo SDK 55, React Native 0.83, and Expo Router for file-based navigation. It shares the same backend as the web frontend and reuses a significant portion of the web codebase.
Getting Started
cd vezta-mobile
pnpm install
pnpm ios # Start on iOS Simulator (requires Xcode)
pnpm android # Start on Android Emulator (requires Android Studio)Environment variables are set in .env:
API_URL=http://localhost:3001/api/v1
WS_URL=ws://localhost:3001EAS build profiles override these per environment (dev uses localhost, preview/production use backend.vezta.io).
Navigation Structure
Expo Router with typed routes and a 5-tab bottom navigation:
| Tab | Path | Screens |
|---|---|---|
| Home | (tabs)/home/ | Feed, trending, balance overview, notifications |
| Markets | (tabs)/markets/ | Browse, search, filter, [slug] market detail + trading sheet |
| Portfolio | (tabs)/portfolio/ | Positions, orders, watchlist, PnL analytics (requires auth) |
| Social | (tabs)/social/ | Leaderboard, copy trade, wallet activity |
| More | (tabs)/more/ | Settings, rewards, events, AI signals, monitor, help |
Auth screens ((auth)/ group) display without the tab bar: login, welcome-back, access-key, biometric setup, onboarding, and waitlist.
Code Sync from Web
The mobile app shares several code layers with vezta-fe. These files are copied manually and must stay in sync:
| Shared Layer | Path | Sync Method |
|---|---|---|
| API types, schemas, hooks | lib/api/gen/ | Copy from vezta-fe after pnpm generate |
| Utilities | lib/utils.ts | Copy from vezta-fe |
| Constants | lib/constants.ts | Copy from vezta-fe |
| TypeScript interfaces | lib/types/ | Copy from vezta-fe |
| Trading helpers | lib/trading/ | Copy from vezta-fe |
| Business logic hooks | hooks/ | Copy from vezta-fe |
Never hand-edit lib/api/gen/. It is fully auto-generated. After any backend API change, regenerate in vezta-fe with pnpm generate, then copy the output to vezta-mobile/lib/api/gen/.
Styling with NativeWind v4
The app uses NativeWind v4 (Tailwind CSS for React Native) with the className prop:
<View className="flex-1 bg-black px-4 pt-6">
<Text className="text-lg font-semibold text-white">
Market Title
</Text>
</View>Design tokens are defined in constants/tokens.ts covering colors, 15 typography styles, spacing, and border radius. The design system mirrors the web: dark mode only, lime-green accent #D4FF2B, sharp corners, Space Grotesk for display text, JetBrains Mono for prices.
Native Modules
Platform-specific functionality lives in lib/native/:
- Push notifications -- Expo Notifications with
#D4FF2Baccent color - Biometric auth -- Face ID / Touch ID via
expo-local-authentication - Deep linking -- Intent filters for market, trader, and invite paths
- Offline sync -- AsyncStorage persister for TanStack Query cache
- Share -- Native share sheet and clipboard operations
- Version check -- App version comparison with server
- OTA updates --
expo-updatesfor over-the-air updates
Auth
Mobile auth uses the same wallet-based flow as the web. Key differences:
- Refresh tokens are stored in SecureStore (not AsyncStorage) -- SecureStore uses the device's secure enclave
- Access tokens are held in-memory
- The API client at
lib/api/client.tshandles automatic 401 refresh-and-retry
Never use AsyncStorage for sensitive data (tokens, keys). SecureStore has a 2048-byte value limit, so only store tokens -- not large data blobs.
Animation Hooks
Reanimated v4 hooks in hooks/animations/:
useNumberRoll-- animated number transitions for price changesuseSkeletonShimmer-- loading placeholder shimmer effectuseTickFlash-- green/red flash on price tick up/downusePressScale-- scale-down animation on button press
Dev Commands
pnpm start # Start Metro bundler
pnpm ios # iOS Simulator
pnpm android # Android Emulator
pnpm test # Jest + React Native Testing Library
pnpm tsc # TypeScript type check (tsc --noEmit)
pnpm lint # ESLintGotchas
- NativeWind v4 uses
className-- not thestyleprop for Tailwind classes - Reanimated v4 requires the babel plugin in
babel.config.js - Bottom sheet (
@gorhom/bottom-sheet) needsGestureHandlerRootViewas an ancestor - Expo Router typed routes require
experiments.typedRoutes: truein app config - Font loading must complete before rendering -- use
expo-splash-screento prevent flash - Always disconnect and reconnect WebSocket on AppState background/foreground transitions
- Decimal values from the API are strings -- parse with
parseFloat()before math - Use
expo-imageinstead of React Native's<Image>for better caching performance