VeztaVezta
Guides

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:3001

EAS build profiles override these per environment (dev uses localhost, preview/production use backend.vezta.io).

Expo Router with typed routes and a 5-tab bottom navigation:

TabPathScreens
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 LayerPathSync Method
API types, schemas, hookslib/api/gen/Copy from vezta-fe after pnpm generate
Utilitieslib/utils.tsCopy from vezta-fe
Constantslib/constants.tsCopy from vezta-fe
TypeScript interfaceslib/types/Copy from vezta-fe
Trading helperslib/trading/Copy from vezta-fe
Business logic hookshooks/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 #D4FF2B accent 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-updates for 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.ts handles 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 changes
  • useSkeletonShimmer -- loading placeholder shimmer effect
  • useTickFlash -- green/red flash on price tick up/down
  • usePressScale -- 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         # ESLint

Gotchas

  • NativeWind v4 uses className -- not the style prop for Tailwind classes
  • Reanimated v4 requires the babel plugin in babel.config.js
  • Bottom sheet (@gorhom/bottom-sheet) needs GestureHandlerRootView as an ancestor
  • Expo Router typed routes require experiments.typedRoutes: true in app config
  • Font loading must complete before rendering -- use expo-splash-screen to 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-image instead of React Native's <Image> for better caching performance

On this page