Skip to Content
Tech StackTech Stack — Mobile

Tech Stack — Mobile

Parent: Tech Stack Overview

Applies to apps/dashboard-mobile — the React Native iOS/Android app targeting client/tenant users only. The DM portal remains web-only.


Framework

TechnologyVersionNotes
React Native0.83.4New Architecture enabled (Fabric + JSI)
ExpoSDK 55Managed workflow — Expo CLI, EAS Build
React19.2Latest React with concurrent features
TypeScriptv5.x strictShared types from @leadmetrics/common

Expo managed workflow — not bare. Build and distribution via EAS (Expo Application Services). Metro bundler for development.

New Architecture (Fabric + JSI): Enabled for better scroll performance on large lists and native module interop for biometric auth.

Bundle ID / Package: ai.leadmetrics.mobile


TechnologyVersionNotes
@react-navigation/nativev7Root navigation container
@react-navigation/native-stackv7Stack navigator
@react-navigation/bottom-tabsv7Bottom tab bar

Deep link scheme: leadmetrics:// — handles push notification taps and OAuth callback redirects via expo-linking.


State Management

TechnologyVersionPurpose
@tanstack/react-queryv5.62Server state — API caching, background refetch, optimistic updates
@tanstack/react-query-persist-clientv5.62Persist query cache to MMKV across app restarts
@tanstack/query-sync-storage-persisterv5.62MMKV storage adapter for persistence

Stale content is shown while fresh data loads. Write mutations (approvals, saves) require connectivity — they are not queued offline.


Forms & Validation

TechnologyVersionNotes
React Hook Formv7.56Uncontrolled forms, same as web dashboard
@hookform/resolversv3.10Zod resolver
Zodv3.24Shared schemas from @leadmetrics/common

Push Notifications

TechnologyPackageVersionPlatform
Firebase Cloud Messaging@react-native-firebase/app + @react-native-firebase/messagingv21Android + iOS (via APNs gateway)
Expo Notificationsexpo-notificationsSDK 55Token management, foreground handling

Token lifecycle: Tokens are registered with the API on first login and updated on rotation via the mobile patterns (crypto.randomUUID for device ID, per-item endpoints). Push device tokens stored in PushDeviceToken model.

Notification types triggered by the platform:

  • Activity/approval created or updated
  • Agent run completed or failed
  • New content ready for client review
  • Budget or goal alerts

Authentication

TechnologyPackagePurpose
Custom JWT (HS256)@/lib/api (fetch wrapper)Email/password login via POST /auth/v1/login — same Fastify API as web portals
Biometric re-authexpo-local-authenticationFace ID / fingerprint for unlock session flow
OAuth browserexpo-web-browserOAuth popups for channel connections
Secure token storageexpo-secure-storeRefresh token stored in OS keychain/keystore

Token flow:

  • Access token: in-memory (React state / query client)
  • Refresh token: expo-secure-store (OS keychain)
  • Silent refresh: AbortController-wrapped fetch with 401 retry
  • Unlock session: unlockSession() auth pattern with biometric gate

Local Storage

TechnologyPackageVersionPurpose
MMKVreact-native-mmkvv3.1Fast key-value store for TanStack Query persistence + settings
expo-secure-storeexpo-secure-storeSDK 55Secure storage for refresh token

Animations & Gestures

TechnologyPackageVersion
Reanimatedreact-native-reanimatedv4.2
Gesture Handlerreact-native-gesture-handlerv2.30
Workletsreact-native-workletsv0.7
Screensreact-native-screensv4.23
Safe Areareact-native-safe-area-contextv5.6

Real-Time (SSE)

TechnologyPackageVersionPurpose
react-native-ssereact-native-ssev1.2SSE client for live agent output and activity status

SSE pre-refresh pattern: token is refreshed before opening the SSE connection to avoid mid-stream 401s.


Markdown

TechnologyPackageVersion
react-native-markdown-displayv7.0Renders strategy, context, and content markdown in native views

UI Patterns

There is no shared component library between web and mobile (web uses custom Tailwind, mobile uses native React Native components).

Mobile-specific patterns:

  • FlatList / SectionList for all scrollable lists — never ScrollView for long lists
  • Pull-to-refresh on all list screens
  • No inline editing (web-only feature) — mobile is read + approve/reject only
  • No DM portal actions — mobile is client-facing only
  • expo-local-authentication for biometric lock screen
  • Per-item API endpoints (not bulk) for all mutations

Shared Code with Web

Shared packageWhat’s reused
@leadmetrics/commonTypeScript types, Zod validation schemas, utility functions

Not shared: UI components, routing, auth flows, storage — all platform-specific. The mobile app does not use @leadmetrics/ui (DOM-based React components).


Build & Distribution

PlatformToolProcess
AndroidEAS Build + GradleBuild → sign → Play Console internal track
iOSEAS Build + XcodeBuild → sign → TestFlight (beta) → App Store

Android emulator setup (dev):

  • JDK 17, Android SDK, AVD with snapshot boot enabled
  • API host: 10.0.2.2 (Android emulator maps to machine’s localhost)
  • MSYS path fix required on Windows for Metro
  • UIAutomator coordinates used for login in automated tests

Metro: restart Metro before emulator login after dependency changes.


Testing

LayerTechnologyVersionScope
UnitJestv29.7Business logic, utilities
Component@testing-library/react-nativev13.3Component behaviour
Frameworkjest-expov55Expo-aware Jest preset

No Playwright E2E for mobile — manual testing + UIAutomator for emulator automation.


Error Monitoring

TechnologyPurpose
Sentry (React Native)Crash reporting, JS error tracking, performance monitoring (production only)

Build-Time Configuration

Mobile apps do not use .env files. Config is injected via expo-constants at build time and via Expo’s app.config.js / EAS environment variables.

import Constants from "expo-constants"; const API_URL = Constants.expoConfig?.extra?.apiUrl ?? "http://10.0.2.2:3003";

Key build variables:

API_URL=https://api.leadmetrics.io APP_ENV=development | staging | production GOOGLE_SERVICES_FILE= # google-services.json path for FCM (Android) SENTRY_DSN= # production only

Secrets never enter the mobile bundle — all sensitive operations go through the Fastify API.

© 2026 Leadmetrics — Internal use only