A production-ready monorepo template for building cross-platform applications with Next.js and React Native (Expo)
Based on Fernando Rojo's work
(You still need to setup supabase manually)
.
├── apps/
│ ├── expo/ # React Native app (Expo)
│ ├── next/ # Next.js web app
│ ├── storybook-native/ # React Native Storybook
│ └── storybook-web/ # Web Storybook
├── packages/
│ ├── app/ # Shared app code (features, providers, navigation)
│ └── api/ # tRPC router and Supabase server client
└── supabase/ # Supabase migrations and config
yarn install
This project uses environment-specific configuration files for development and production. Each app has separate .env.development and .env.production files.
Next.js (apps/next/):
- .env.development - Development environment variables (auto-loaded when NODE_ENV=development)
- .env.production - Production environment variables (auto-loaded when NODE_ENV=production)
- .env.local - Local overrides (gitignored, highest priority, never commit this file)
Expo (apps/expo/):
- .env.development - Development environment variables (loaded via helper script)
- .env.production - Production environment variables (loaded via helper script)
- .env.local - Local overrides (gitignored, highest priority, never commit this file)
Note: Next.js automatically loads environment files based on
NODE_ENV. Expo uses a custom helper script (scripts/load-env.js) to achieve the same behavior.
apps/next/.env.development and apps/expo/.env.development:http://127.0.0.1:54321yarn supabase:statusFor Expo, set EXPO_PUBLIC_API_URL to http://localhost:3000/api/trpc (or your machine's IP for physical devices)
Update production values in apps/next/.env.production and apps/expo/.env.production:
Configure production Sentry DSNs
Create .env.local files (optional, for local overrides):
bash
# These files are gitignored and should contain only your local secrets
touch apps/next/.env.local
touch apps/expo/.env.local
Next.js:
- NEXT_PUBLIC_SUPABASE_URL - Your Supabase project URL
- NEXT_PUBLIC_SUPABASE_ANON_KEY - Your Supabase anon/public key
- SUPABASE_SERVICE_ROLE_KEY - (Optional) For server-side operations with elevated permissions
- NEXT_PUBLIC_SENTRY_DSN - (Optional) Sentry DSN for client-side error tracking
- SENTRY_DSN - (Optional) Server-side Sentry DSN
- SENTRY_ORG - (Optional) Sentry organization slug
- SENTRY_PROJECT - (Optional) Sentry project slug
Expo:
- EXPO_PUBLIC_SUPABASE_URL - Your Supabase project URL
- EXPO_PUBLIC_SUPABASE_ANON_KEY - Your Supabase anon/public key
- EXPO_PUBLIC_API_URL - Your Next.js API URL (for tRPC)
- EXPO_PUBLIC_SENTRY_DSN - (Optional) Sentry DSN for error tracking
Root (for lingui-ai-translate):
- OPENAI_API_KEY - (Optional) OpenAI API key for automated translations. Get your key from platform.openai.com
Start Supabase locally:
yarn supabase:start
This will: - Start all Docker containers - Run database migrations - Load seed data
Get your local environment variables:
yarn supabase:status
Access Supabase Studio at: http://localhost:54323
# Link to your Supabase project (only needed once)
supabase link --project-ref your-project-ref
# Push migrations to production
supabase db push
⚠️ Important: The template includes a seed file. Review and modify supabase/seed.sql before deploying to production.
Update the project ID in supabase/config.toml:
project_id = "your-project-name"
This helps distinguish different Supabase projects on the same machine.
Update the production API URL in apps/expo/utils/trpc/client.tsx:
return process.env.EXPO_PUBLIC_API_URL || 'https://your-domain.com/api/trpc'
yarn web
This will:
- Automatically load .env.development and .env.local (if exists) based on NODE_ENV=development
- Start the Next.js dev server
- Run on http://localhost:3000
You can also run directly from the apps/next directory:
cd apps/next
yarn dev
Note: Next.js automatically loads environment files based on
NODE_ENV. Nodotenv-clineeded.
First, build a dev client:
cd apps/expo
yarn ios
# or
yarn android
Then, from the root:
yarn native
Or run directly from the apps/expo directory:
cd apps/expo
yarn start
The Expo scripts automatically load .env.development and .env.local (if exists) via the load-env.js helper script.
apps/nextNEXT_PUBLIC_SUPABASE_URL - Your production Supabase URLNEXT_PUBLIC_SUPABASE_ANON_KEY - Your production Supabase anon keySUPABASE_SERVICE_ROLE_KEY - Your production service role keySENTRY_* - (Optional) Sentry configuration for productionNote: Vercel automatically sets
NODE_ENV=productionduring build, so Next.js will load.env.productionautomatically. However, you should set production values in Vercel's environment variables dashboard for security.
cd ../.. && npx turbo run build --filter=next-app.nextOr use the deploy button above after updating the repository URL.
Deploy using EAS (Expo Application Services):
Install EAS CLI:
bash
npm install -g eas-cli
Login to EAS:
bash
eas login
The eas.json file is already configured with build profiles:
- development: Development client builds (simulator/APK)
- preview: Internal distribution builds (APK)
- production: Production app store builds (App Bundle/IPA)
bash
cd apps/expo
eas secret:create --scope project --name EXPO_PUBLIC_SUPABASE_URL --value "https://your-project.supabase.co"
eas secret:create --scope project --name EXPO_PUBLIC_SUPABASE_ANON_KEY --value "your-production-anon-key"
eas secret:create --scope project --name EXPO_PUBLIC_API_URL --value "https://your-domain.com/api/trpc"
eas secret:create --scope project --name EXPO_PUBLIC_SENTRY_DSN --value "your-sentry-dsn" # OptionalOr configure them in the EAS dashboard.
apps/expo/eas.json and update the submit.production section with your:# Build for iOS npm run eas:build:ios:profile
# Build for Android npm run eas:build:android:profile
# Build for both platforms npm run eas:build:all -- --profile production ```
# Submit iOS build npm run eas:submit:ios
# Submit Android build npm run eas:submit:android ```
Publish over-the-air updates without rebuilding:
cd apps/expo
# Publish update to production channel
npm run eas:update:republish
# Or use interactive mode
npm run eas:update
The project includes several EAS-related scripts in apps/expo/package.json:
Build:
- npm run eas:build:android - Build for Android
- npm run eas:build:ios - Build for iOS
- npm run eas:build:all - Build for both platforms
- npm run eas:build:android:profile - Build Android with production profile
- npm run eas:build:ios:profile - Build iOS with production profile
Submit:
- npm run eas:submit:android - Submit Android build to Play Store
- npm run eas:submit:ios - Submit iOS build to App Store
Update:
- npm run eas:update - Publish OTA update (interactive)
- npm run eas:update:republish - Publish update to production branch
Configuration:
- npm run eas:configure - Configure EAS Build
This template includes Sentry for error tracking. To enable:
This template uses Lingui for i18n. Supported locales are configured in packages/app/lingui.config.js.
The typical i18n workflow consists of three steps:
Extract translatable strings from your codebase:
yarn lingui:extract
This scans your code for t macros and other Lingui translation functions and generates .po files in packages/app/locales/.
This template includes lingui-ai-translate for automated translations using OpenAI's API.
Setup:
For root .env (used by lingui-ai-translate):
bash
OPENAI_API_KEY=your-openai-api-key-here
Or export it in your shell:
bash
export OPENAI_API_KEY=your-openai-api-key-here
bash
yarn lingui:translate
This will automatically translate all messages in your .po files to all configured locales using OpenAI's API. The translations preserve placeholders and formatting.
You can also manually edit the .po files in packages/app/locales/ to translate messages yourself.
After translating, compile the messages for runtime use:
yarn lingui:compile
This generates optimized .js files that are used by your application at runtime.
This template includes Storybook for both web and React Native, allowing you to develop and test UI components in isolation.
Start the web Storybook to view and test components in a browser environment:
yarn storybook:web
This will start Storybook on http://localhost:6006
Start the React Native Storybook to view and test components in a mobile environment:
yarn storybook:native
This will start Storybook on http://localhost:7007 and open the Expo app with Storybook UI integrated.
You can also run it directly from the apps/storybook-native directory:
cd apps/storybook-native
yarn storybook
# or run the Expo app directly
yarn ios
# or
yarn android
Stories are automatically loaded from:
- apps/storybook-web/src/**/*.stories.tsx (web stories)
- apps/storybook-native/src/**/*.stories.tsx (native stories)
- packages/ui/src/**/*.stories.tsx (shared UI package stories)
packages/app/tamagui.config.ts$ claude mcp add Nexpo \
-- python -m otcore.mcp_server <graph>