Overview
A full-featured Expo and React Native starter with Supabase, RevenueCat, app services, and EAS distribution wired together.
The Expo (React Native) template is a full-featured mobile starter for teams that want one TypeScript codebase across iOS, Android, and web. It ships with Expo Router navigation, a Supabase backend, RevenueCat purchase flows, account-bound entitlements, analytics, crash reporting, launch gates, push inbox plumbing, deep links, and a demo surface you can remove once your own product screens take over.
The app plumbing above is real and integrated. Some surfaces are intentionally Example or Removable demo features, and you should replace them with your product workflow before launch.
The Expo template is the original cross-platform mobile starter. The Swift and
Kotlin templates were ported from it, but their native stacks have diverged, so
these docs describe the TypeScript source in ../soar-expo.
Configuration model
The app reads public client configuration from EXPO_PUBLIC_* variables and
build-time metadata from app.config.ts. The Supabase URL and anon key are the
first values to set because lib/supabase.ts constructs the client at module
load time and throws if either is missing. Other providers check their own keys
and no-op, hide actions, or show "not configured" feedback when absent.
EXPO_PUBLIC_* values are bundled into the app. Use them only for publishable
client keys such as Supabase anon keys, RevenueCat public SDK keys, PostHog
project keys, and Sentry DSNs. Server secrets belong in Supabase Edge secrets
or EAS secrets.
Tech stack
| Layer | Choice |
|---|---|
| Runtime | React Native 0.81.5, React 19.1, Expo SDK 54 |
| App shell | Expo Router 6 with typed routes, route groups, modal screens, and Metro static web output |
| Native posture | New Architecture enabled, Android edge-to-edge, portrait orientation |
| Styling | NativeWind 4, Tailwind CSS, React Native Reusables, @rn-primitives/*, CVA |
| Backend | Supabase JS 2 typed with types/database.ts |
| Data cache | TanStack Query 5 with AsyncStorage persistence and NetInfo online state |
| Forms | react-hook-form + zod schemas |
| Session storage | expo-secure-store through ChunkedSecureStore on native; localStorage on web |
| Purchases | RevenueCat via react-native-purchases; app entitlement reads Supabase subscriptions |
| Analytics | PostHog React Native with typed event helpers, identify/reset, feature flags |
| Crash reporting | Sentry React Native Expo plugin, navigation integration, Expo update tags, sourcemaps |
| Localization | i18n-js + expo-localization, English and Simplified Chinese |
| Notifications | expo-notifications permission flow, local inbox, Expo push-token retrieval |
| Device services | expo-local-authentication, expo-haptics, expo-store-review, tracking transparency |
| Media | expo-image, expo-image-picker, expo-document-picker, expo-image-manipulator |
| Launch and updates | Lottie splash asset, app-version gate, expo-updates, EAS Build/Submit |
Feature status
Status labels used throughout this root:
- Integrated — wired in the app; activates when its key or service is configured.
- Example — a working reference feature meant to be replaced.
- Removable demo — useful for evaluating the template, but safe to delete.
- Native-only — needs a native iOS/Android build and no-ops or degrades on web.
- Device-only — needs a physical device for the full behavior.
| Feature | Status | Platform support | Notes |
|---|---|---|---|
| Email auth | Integrated | iOS / Android / Web / Expo Go | Supabase email/password, sign-up OTP, login OTP, forgot/reset, change email/password |
| Apple Sign-In | Native-only | iOS native builds | expo-apple-authentication; unavailable on Android and web |
| Google Sign-In | Native-only | iOS / Android native builds | Optional native module; hidden by runtime failure on web and requires Google OAuth IDs |
| Profile | Example | iOS / Android / Web / Expo Go | Profile row, avatar upload, signed URLs, account settings |
| Todos | Example | iOS / Android / Web / Expo Go | Supabase CRUD with TanStack Query pagination and optimistic cache updates |
| Upload sample | Example | iOS / Android / Web varies | Document picker to sample-uploads/<user-id>/... plus sample_uploads table row |
| Subscriptions | Integrated | iOS / Android native builds | RevenueCat purchase/restore; entitlement source of truth is Supabase |
| Redeem code | Native-only | iOS / Android native builds | iOS redemption sheet; Android opens Google Play redeem URL; web shows guidance |
| Push inbox and permissions | Device-only | Native device best | Local inbox, permissions, Android channels, Expo push token; server sending is not wired by default |
| Deep links | Integrated | iOS / Android / Web | Custom Expo URL plus HTTPS links when LINKING_DOMAIN is set |
| Analytics | Integrated | iOS / Android / Web / Expo Go | PostHog screen tracking, typed events, identify/reset, feature flags |
| Crash reporting | Integrated | iOS / Android / Web / Expo Go | Sentry enabled when DSN exists; native frame tracking disabled in Expo Go |
| App gates | Integrated | iOS / Android / Web | Onboarding, legal consent, version gate, auth auto-dismiss |
| Biometric lock | Native-only | iOS / Android device | Uses expo-local-authentication; requires enrolled biometrics |
| Permissions primer | Integrated | iOS / Android / Web fallbacks | Notifications, photos, camera, ATT; web tracking fallback returns granted |
| Store review | Native-only | iOS / Android store-capable builds | Web fallback is a no-op |
| Offline banner and cache | Integrated | iOS / Android / Web | NetInfo drives Query online state; query cache persists with AsyncStorage |
| Toast and haptics | Integrated | iOS / Android / Web fallback | Toast provider is global; haptics are native where available |
| i18n | Integrated | iOS / Android / Web / Expo Go | English and Simplified Chinese with runtime language state |
| Theming | Integrated | iOS / Android / Web / Expo Go | NativeWind color scheme, persisted preference, iOS-style color tokens |
| Refer a friend | Example | iOS / Android / Web varies | Share sheet using generated app links |
| Component gallery | Example | iOS / Android / Web / Expo Go | React Native Reusables component catalog |
| Showcase | Removable demo | iOS / Android / Web / Expo Go | Capability catalog and demo links for evaluating the starter |
| Animated splash | Integrated | iOS / Android / Web | Splash assets configured in Expo config; initial app surfaces match grouped background |
App surface map
The app is composed from the root outward:
- Root providers —
app/_layout.tsxmountsQueryProvider,AuthProvider,AnalyticsProvider,ConsentProvider,OnboardingProvider,EntitlementProvider,PurchasesProvider,I18nProvider,ThemePreferenceProvider,NotificationsProvider,BiometricProvider,ToastProvider, bottom sheets, portals, and Sentry navigation tracking. - Gate stack —
OnboardingGate,AuthGate,VersionGate,ConsentGate, andBiometricGaterun around the Expo RouterStack. Auth is guest-first: most screens are browsable signed out, and signed-in-only actions useSignInGate. - Auth stack —
app/(auth)contains login, sign-up, forgot-password, and reset-password screens. - Main shell —
app/(tabs)has four tabs: Home, Component, Showcase, and Me. A drawer exposes Home, Component, Showcase, Todos, Upload sample, Refer, Profile, Account Security, Notifications, and Sign In. - Pushed screens —
todos,profile,account-security,notifications,permissions,refer,upload-sample,legal/*,manage-subscription, and showcase detail routes. - Modals —
paywallandredeem-codeare modal stack screens.
Cross-platform entitlement
Paid access is account-bound. RevenueCat handles purchase, restore, offerings,
and local CustomerInfo, but the app gates access from the shared Supabase
subscriptions table through useEntitlement().isEntitled. That table is also
the read model used by the desktop template's Creem webhook, so entitlement can
follow the same Supabase user across platforms.
The single rule: RevenueCat is the purchase mechanism, not the source of truth for gating. The server-side row wins.
Start here
Installation
Install dependencies and run iOS, Android, web, or Expo Go.
Configuration
Environment keys, app variants, links, and app config.
Supabase Setup
Auth, migrations, storage, Edge Functions, and generated types.
Authentication
Email auth, Apple, Google, sessions, and account security.
Subscriptions
RevenueCat purchases with Supabase entitlement state.
EAS Build
Development, preview, production builds, channels, and OTA updates.
Store Release
Submit iOS, Android, web, and update releases.
