React Native (Expo)
The Expo stack — a React Native app on Expo SDK 56 with Expo Router and NativeWind, standalone or in a monorepo with the same modular Express backend, plus mobile screens for auth, payments, AI, and tRPC.
The React Native (Expo) stack generates a mobile app on Expo SDK 56 using Expo Router (file-based routing) and NativeWind (Tailwind CSS for React Native). Because a mobile app is a client — it can't host a database, ORM, or webhooks — the stack pairs it with the same Express backend as the Vite + Node stack, and reuses every express-* module. Choose it at the first nextron create prompt.
Layouts
- App only — a standalone Expo app at the project root that talks to an external backend via
EXPO_PUBLIC_API_URL. Backend files are never generated; only the client screens and services are. - Monorepo — a pnpm workspace with
apps/mobile(the Expo app) andapps/api(the same modular Express backend). The mobile app defaults toEXPO_PUBLIC_API_URL=http://localhost:4000, matching the API's default port.
Mobile app architecture
The Expo app follows the same separation of concerns as the web app in the Vite + Node stack, so screens stay thin and API access stays in one place:
app/ # Expo Router screens (file-based routing)
_layout.tsx # root layout — renders <Providers><Stack/></Providers>
index.tsx # home screen (modules inject nav links)
(auth)/ # route groups added by modules (e.g. sign-in/up)
src/
shared/
env.ts # zod-validated EXPO_PUBLIC_* env
lib/api.ts # the HTTP transport — ONLY services import this
providers.tsx # composes context providers (modules inject here)
components/NavLink.tsx # home-screen link card
modules/
<feature>/
services/ # all API calls for the feature live here
components/ # feature UI
hooks/ # feature hooks- Never call
api()from a screen. Screens call a service (modules/<feature>/*.service.ts); only services import theapitransport. This keeps request/response shapes, endpoints, and error handling in one testable layer. - Routing is file-based: a module drops a screen file under
app/and it is routed automatically — no route registry to edit. Modules add home-screen links at thenextron:mobile-navmarker inapp/index.tsx. - Providers are composed in
src/shared/providers.tsx. Modules wrap their provider (session, tRPC, Stripe) at thenextron:mobile-providers-open/nextron:mobile-providers-closemarkers — the same marker mechanism the backend uses for routes. - The
@/alias maps tosrc(configured intsconfig.json). - Auth wires a token provider into the transport, so every request automatically carries
Authorization: Bearer <token>once signed in — services never set it by hand. The token is persisted in the device keychain viaexpo-secure-store.
Environment
Only variables prefixed EXPO_PUBLIC_ are inlined into the app bundle — never put secrets in the client. The generated src/shared/env.ts validates them with zod at startup.
| Variable | Purpose |
|---|---|
EXPO_PUBLIC_API_URL | Base URL of the backend the app talks to |
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY | Stripe publishable key (added with the Stripe module) |
Modules
Every module's backend files target apps/api, exactly as in the Vite + Node stack; its mobile screens and services target apps/mobile (or the project root for app-only projects).
| Module | Mobile surface |
|---|---|
express-auth | Sign-in / sign-up screens, a useSession() hook, and secure-store token storage. Requires a database module for the backend user store. |
stripe-express | A native PaymentSheet checkout screen (@stripe/stripe-react-native) and a StripeProvider. |
polar-express | A checkout screen that opens Polar's hosted checkout in the browser (expo-web-browser). |
express-ai | A chat screen that streams from the backend POST /api/ai/chat. |
express-trpc | A typed tRPC client + React Query provider. Import AppRouter from the backend for end-to-end type-safety. |
express-database, express-inngest | Backend only — no mobile surface. |
For the backend behavior of these modules (endpoints, webhooks, databases, JWT auth), see the Express backend guide.
Commands
# Monorepo (from the workspace root)
pnpm dev # runs the API and the Expo dev server in parallel
pnpm dev:api # API only
pnpm dev:mobile # Expo dev server only
# Inside the app (app-only, or apps/mobile)
npx expo start # start Metro
npm run ios # / android / web
pnpm typecheckDependency versions
The generated package.json pins a coherent Expo SDK 56 set, but transitive native versions move fast. If install or the dev build complains about a version, run npx expo install --check (or npx expo install <package>) inside the app to reconcile every dependency with your installed SDK — Expo's resolver is the source of truth.
Adding modules
Modules are stack-aware. The express-* modules apply to both the Vite + Node and Expo stacks; Next.js modules (auth, trpc, dashboard, …) cannot be added to an Expo project. In a monorepo, run nextron add from apps/api, where nextron.config.ts lives — the backend side is wired automatically; add the matching mobile screens under apps/mobile as needed.