ProjectR Docs
Mobile

Mobile

Developer reference for the Expo app.

apps/mobile is THERAW, the customer-facing app. It runs Expo SDK 58 (expo ~58.0.19), React Native 0.88.0, React 19.3.0, react-native-gesture-handler 3.3, Reanimated 4.7, expo-router 58 with typed routes and native tabs from expo-router/native-tabs, @expo/ui for native sheets, menus and the date picker, uniwind + HeroUI Native, Zustand + MMKV, Convex reactive subscriptions, and Better Auth with phone OTP + Google + Apple. Version 1.10.0, package com.tipeer.projectr.

Read in order

  • Structure — route groups, features, boundary rules.
  • Data & Auth — subscriptions, the sign-in sheet, the account stage, session rules.
  • Notifications for the pre-prompt rule, push token registration, and the Android channel.
  • Commerce — cart, checkout sheet, orders.
  • Releases — EAS, fingerprints, and the release command.
  • Testing — Jest and Maestro.
  • Environment — env.js and production invariants.

The five rules people break first

  1. No React Query — server state is Convex useQuery subscriptions; never manually refetch.
  2. Backend changes happen in packages/backend — validator changes re-type this app immediately; run bun run check-all after.
  3. convex dev never runs here. It runs only from packages/backend.
  4. Styling is uniwind, NOT NativeWind. Never install NativeWind or react-native-css, whichever skill or guide suggests it. Either one adds a second Tailwind runtime that conflicts with uniwind.
  5. Money math is integer cents — parse backend decimal strings once with toCents (src/shared/utils/money.ts), convert back only at the display boundary.

SDK 58 workarounds

SDK 58 is stable (moved 2026-10-16). A few workarounds wait on upstream releases, and each comes out at its own trigger:

  • The Metro redirect of pressto's gesture-handler import to src/shared/components/ui/utils/pressto-gesture-handler.ts, which exports LegacyBaseButton as BaseButton. Without it, dragging a product card or the Home banner to scroll the page on Android opens it instead. Remove it once pressto supports gesture handler 3 (pressto issue #37).
  • The asset registry mock in jest-setup.ts, until jest-expo mocks React Native 0.88's private registry module itself.
  • The React Native Directory exclusion in apps/mobile/package.json (expo.doctor.reactNativeDirectoryCheck.exclude). Directory marks react-native-render-html unmaintained, and the entry stays until the app replaces the library. With it, bun run doctor and the CI doctor check pass all 20 checks.

Stack headers stay opaque on iOS 26 through opaqueHeaderOptions in src/shared/utils/header-styles.ts. expo-router 58.0.20 made a header transparent whenever a screen sets no header background, and our screens are laid out for an opaque bar.

One upgrade follow-up has no stable trigger. Gesture handler 3 deprecates the Gesture builder, and price-filter-sheet.tsx and notification-row.tsx still call Gesture.Pan(). Moving them to usePanGesture takes its own PR and a device check of the price thumbs inside the Android sheet and of the notification swipe.

On this page