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)
- Click "Use this template" on GitHub
- Clone your new repository
- Follow the Setup Instructions below
- Solito - Cross-platform navigation between Next.js and React Native
- Next.js 16 - React framework for production
- Expo SDK 54 - React Native framework
- React 19 with React Compiler
- tRPC - End-to-end typesafe APIs
- Supabase - Backend as a service (database, auth, storage)
- Lingui - Internationalization (i18n)
- Tamagui - Universal design system
- Storybook - Component development and testing (web & native)
- Testing - End-to-end (Playwright for web, Maestro for mobile), database (pgTAP), and API/integration (Vitest) tests
- Sentry - Error tracking and monitoring
- Turborepo - Monorepo build system
- Biome - Fast formatter and linter
- Husky - Git hooks
.
βββ 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
- Node.js 20+ and Yarn 4.7.0+
- Docker (for local Supabase development)
- Git
yarn installThis 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 whenNODE_ENV=development).env.production- Production environment variables (auto-loaded whenNODE_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.
-
Update development values in
apps/next/.env.developmentandapps/expo/.env.development:- Set your local Supabase URL:
http://127.0.0.1:54321 - Get your local Supabase anon key by running
yarn supabase:status - For Expo, set
EXPO_PUBLIC_API_URLtohttp://localhost:3000/api/trpc(or your machine's IP for physical devices)
- Set your local Supabase URL:
-
Update production values in
apps/next/.env.productionandapps/expo/.env.production:- Replace placeholder values with your actual production Supabase credentials
- Set production API URLs
- Configure production Sentry DSNs
-
Create
.env.localfiles (optional, for local overrides):# 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 URLNEXT_PUBLIC_SUPABASE_ANON_KEY- Your Supabase anon/public keySUPABASE_SERVICE_ROLE_KEY- (Optional) For server-side operations with elevated permissionsNEXT_PUBLIC_SENTRY_DSN- (Optional) Sentry DSN for client-side error trackingSENTRY_DSN- (Optional) Server-side Sentry DSNSENTRY_ORG- (Optional) Sentry organization slugSENTRY_PROJECT- (Optional) Sentry project slug
Expo:
EXPO_PUBLIC_SUPABASE_URL- Your Supabase project URLEXPO_PUBLIC_SUPABASE_ANON_KEY- Your Supabase anon/public keyEXPO_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:startThis will:
- Start all Docker containers
- Run database migrations
- Load seed data
Get your local environment variables:
yarn supabase:statusAccess Supabase Studio at: http://localhost:54323
- Create a project at supabase.com
- Get your project URL and API keys from the project settings
- Update your environment variables with production values
- Push your database schema:
# Link to your Supabase project (only needed once)
supabase link --project-ref your-project-ref
# Push migrations to production
supabase db pushsupabase/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 webThis will:
- Automatically load
.env.developmentand.env.local(if exists) based onNODE_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 devNote: Next.js automatically loads environment files based on
NODE_ENV. Nodotenv-clineeded.
First, build a dev client:
cd apps/expo
yarn ios
# or
yarn androidThen, from the root:
yarn nativeOr run directly from the apps/expo directory:
cd apps/expo
yarn startThe Expo scripts automatically load .env.development and .env.local (if exists) via the load-env.js helper script.
-
Connect your repository to Vercel
-
Set the root directory to
apps/next -
Configure environment variables in Vercel dashboard:
NEXT_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 production
Note: 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. -
Vercel will automatically detect:
- Framework: Next.js
- Build Command:
cd ../.. && npx turbo run build --filter=next-app - Output Directory:
.next
Or use the deploy button above after updating the repository URL.
Deploy using EAS (Expo Application Services):
-
Install EAS CLI:
npm install -g eas-cli
-
Login to EAS:
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)
-
Configure environment variables in EAS:
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" # Optional
Or configure them in the EAS dashboard.
-
Update submit credentials (optional, for automated submissions): Edit
apps/expo/eas.jsonand update thesubmit.productionsection with your:- Apple ID, App Store Connect App ID, and Team ID (for iOS)
- Google Play service account key path (for Android)
-
Build for production:
cd apps/expo # 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 to app stores:
cd apps/expo # 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:updateThe project includes several EAS-related scripts in apps/expo/package.json:
Build:
npm run eas:build:android- Build for Androidnpm run eas:build:ios- Build for iOSnpm run eas:build:all- Build for both platformsnpm run eas:build:android:profile- Build Android with production profilenpm run eas:build:ios:profile- Build iOS with production profile
Submit:
npm run eas:submit:android- Submit Android build to Play Storenpm 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:
- Create a project at sentry.io
- Get your DSN from project settings
- Set the environment variables (see above)
- Sentry will automatically initialize in both apps
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 messages - Extract translatable strings from your code
- Translate messages - Translate messages using AI or manual translation
- Compile messages - Compile translations for runtime use
Extract translatable strings from your codebase:
yarn lingui:extractThis 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:
-
Get your OpenAI API key from platform.openai.com
-
Add it to your environment variables:
For root
.env(used by lingui-ai-translate):OPENAI_API_KEY=your-openai-api-key-here
Or export it in your shell:
export OPENAI_API_KEY=your-openai-api-key-here -
Run the translation command:
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:compileThis 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:webThis will start Storybook on http://localhost:6006
Start the React Native Storybook to view and test components in a mobile environment:
yarn storybook:nativeThis 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 androidStories 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)
- Tamagui Integration - Both Storybook apps use the shared Tamagui config from
packages/app/tamagui.config.ts - Monorepo Support - Stories from shared packages are automatically loaded
- TypeScript Support - Full TypeScript support for stories
- Addons - Web Storybook includes accessibility, docs, and testing addons
When writing stories that should work in both web and React Native:
import type { Meta, StoryObj } from '@storybook/react' // web
// or '@storybook/react-native' for native-only stories
import { YourComponent } from './YourComponent'
const meta = {
title: 'UI/YourComponent',
component: YourComponent,
// ... rest of config
} satisfies Meta<typeof YourComponent>
export default metaFor platform-specific stories, use platform-specific story files or conditional imports.
This template includes comprehensive testing infrastructure covering end-to-end tests (web and mobile), database tests, and API/integration tests.
Playwright-based end-to-end tests for the Next.js web application.
Location: apps/next/e2e/
Coverage:
- Home page across all locales
- User pages
- API routes
- Navigation and routing
- Accessibility features
Run tests:
# From root
yarn test:e2e
# From apps/next directory
cd apps/next
yarn test:e2e
# Interactive UI mode
yarn test:e2e:ui
# Headed mode (see browser)
yarn test:e2e:headedDocumentation: See apps/next/e2e/README.md for detailed E2E testing guide.
Maestro-based end-to-end tests for the Expo/React Native mobile application.
Location: apps/expo/.maestro/
Coverage:
- Navigation (home screen, user detail screen)
- Authentication (sign in, sign out, protected endpoints)
- Theme (dark/light mode toggle)
- Locale (internationalization and locale switching)
- Integration (full user flows)
Prerequisites:
-
Install Maestro CLI:
# macOS brew tap mobile-dev-inc/tap brew install maestroSee Maestro installation guide for other platforms.
-
Build and install the app (development build requires Metro bundler):
cd apps/expo # Terminal 1: Start Metro bundler yarn start # Terminal 2: Build and install app yarn ios # or yarn android
Run tests:
# From root directory
yarn test:e2e:mobile
# From apps/expo directory
cd apps/expo
yarn test:e2e
# List available devices
yarn test:e2e:device:list
# Run a specific test file
maestro test .maestro/navigation/home-screen.yamlImportant: For development builds, ensure Metro bundler is running before executing Maestro tests. Also make sure to run your web and supabase instance to avoir network errors.
Documentation: See apps/expo/.maestro/README.md for detailed Maestro testing guide.
pgTAP-based tests for database schema, security, and functionality.
Location: supabase/tests/database/
Coverage:
- Schema structure (tables, columns, types, constraints)
- Row Level Security (RLS) policies
- Database triggers
- Database functions
Run tests:
# From root
yarn test:db
# Or directly
supabase test dbPrerequisites:
- Supabase CLI must be installed
- Local Supabase must be running (
yarn supabase:start)
Documentation: See supabase/tests/README.md for detailed database testing guide.
Vitest-based tests for tRPC procedures and API endpoints.
Location: packages/api/src/__tests__/
Coverage:
- tRPC procedures (hello, userList, userById, userCreate, testAuth)
- Authentication flows
- Error handling
- API integration
Run tests:
# From root
yarn test:api
# From packages/api directory
cd packages/api
yarn test
# Watch mode
yarn test:watch
# Interactive UI
yarn test:ui
# Coverage report
yarn test:coverageDocumentation: See packages/api/README_TESTING.md for detailed API testing guide.
Run all tests (database, API, and E2E) from the root:
# Run all tests (database, API, and web E2E)
yarn test
# Watch mode
yarn test:watch
# Coverage report
yarn test:coverage
# Mobile E2E tests (from apps/expo directory)
cd apps/expo
yarn test:e2e- Web E2E Tests: Configured in
apps/next/playwright.config.ts - Mobile E2E Tests: Configured in
apps/expo/.maestro/config.yaml - Database Tests: Uses pgTAP via Supabase CLI
- API Tests: Configured in
packages/api/vitest.config.ts
-
Install dependencies:
yarn install
-
Start Supabase (for database and API tests):
yarn supabase:start
-
Run tests:
# Database tests yarn test:db # API tests yarn test:api # Web E2E tests (requires Next.js dev server) yarn web # In separate terminal yarn test:e2e:web # In another terminal # Mobile E2E tests (requires Metro bundler and app installed) cd apps/expo yarn start # Terminal 1: Start Metro yarn ios # Terminal 2: Build and install app yarn test:e2e # Terminal 3: Run Maestro tests
- Testing Summary - Overview of all testing setup
- Testing Quick Start - Quick start guide
- Testing Plan - Comprehensive testing strategy
If you're installing a JavaScript-only dependency that will be used across platforms, install it in packages/app:
cd packages/app
yarn add date-fns
cd ../..
yarnIf you're installing a library with any native code, you must install it in apps/expo:
cd apps/expo
yarn add react-native-reanimated
cd ../..
yarnYou can also install the native library inside of packages/app if you want to get autoimport for that package inside of the app folder. However, you need to be careful and install the exact same version in both packages. If the versions mismatch at all, you'll potentially get terrible bugs. This is a classic monorepo issue.
To check for and fix version mismatches, use the check-deps command:
# Check for version mismatches
yarn check-deps
# Automatically fix version mismatches
yarn check-deps:fixAlways run yarn check-deps after installing dependencies to ensure all packages are using compatible versions.
# Install dependencies
yarn install
# Start Next.js dev server (loads .env.local automatically)
yarn web
# Start Expo dev server (loads .env automatically)
yarn native
# Start Storybook
yarn storybook:web # Web Storybook (port 6006)
yarn storybook:native # React Native Storybook (port 7007)
# Testing
yarn test # Run all tests (database, API, web E2E)
yarn test:db # Run database tests (pgTAP)
yarn test:api # Run API/integration tests (Vitest)
yarn test:e2e:web # Run web end-to-end tests (Playwright)
yarn test:watch # Watch mode for all tests
yarn test:coverage # Generate coverage report
yarn test:e2e:mobile # Run mobile end-to-end tests (Maestro)
# Run linter
yarn lint
# Fix linting issues
yarn lint:fix
# Format code
yarn format
# EAS commands (from apps/expo directory)
cd apps/expo
npm run eas:build:android # Build for Android
npm run eas:build:ios # Build for iOS
npm run eas:submit:android # Submit Android build
npm run eas:submit:ios # Submit iOS build
npm run eas:update # Publish OTA update
# Lingui commands
yarn lingui:extract # Extract translatable messages from code
yarn lingui:translate # Translate messages using AI (requires OPENAI_API_KEY)
yarn lingui:compile # Compile translations for runtime use
# Supabase commands
yarn supabase:start # Start local Supabase
yarn supabase:stop # Stop local Supabase
yarn supabase:status # Show connection details
yarn supabase:reset # Reset database and run migrations
yarn supabase:logs # View logs
# Dependency management
yarn check-deps # Check for version mismatches across packages
yarn check-deps:fix # Automatically fix version mismatchesNext.js:
- Automatically loads
.env.developmentwhenNODE_ENV=development(default foryarn dev) - Automatically loads
.env.productionwhenNODE_ENV=production(default foryarn buildandyarn start) - Always loads
.env.locallast (highest priority, gitignored) - No
dotenv-clineeded - Next.js handles this natively
Expo:
- Uses
scripts/load-env.jshelper to load environment files based onNODE_ENV - Loads
.env.developmentwhenNODE_ENV=development(default foryarn start,yarn ios,yarn android) - Loads
.env.productionwhenNODE_ENV=production(foryarn build:ios,yarn build:android) - Always loads
.env.locallast (highest priority, gitignored)
All yarn commands within each app directory automatically load the appropriate environment files based on NODE_ENV, so you don't need to manually source or export environment variables.
- Solito Documentation
- Next.js Documentation
- Expo Documentation
- Supabase Documentation
- tRPC Documentation
- Turborepo Documentation
This template is available as open source under the terms of the MIT License.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Review and customize
supabase/seed.sqlbefore deploying to production - Update all hardcoded URLs and API endpoints in your codebase
- Set up proper environment variables for all environments
- Configure Sentry for production error tracking
- Set up proper database backups in Supabase
- Review security settings in Supabase dashboard
If you encounter issues with local Supabase:
# Reset everything
yarn supabase:stop
docker system prune -a --volumes
yarn supabase:startIf ports 3000, 54321, 54322, or 54323 are in use, you can:
- Change ports in respective config files
- Stop the conflicting services
Clear caches and rebuild:
# Clear Turbo cache
rm -rf .turbo
# Clear Next.js cache
rm -rf apps/next/.next
# Reinstall dependencies
rm -rf node_modules apps/*/node_modules packages/*/node_modules
yarn installFuture enhancements planned for this template:
- GitHub Actions workflows for automated:
- Linting and code quality checks
- Running test suites (database, API, E2E)
- Automated deployments to Vercel and EAS
- Dependency security scanning
- Build verification across all packages
- Optional integration with analytics platforms:
- PostHog - Product analytics and feature flags
- Amplitude - User behavior analytics
- Configurable opt-in/opt-out for privacy compliance
- Developer documentation integration:
- Docusaurus or Mintlify integration
- Automated API documentation from tRPC routes
- Component documentation from Storybook
- Automated deployment of docs site
- Built-in performance monitoring and profiling:
- Web Vitals tracking
- React Native performance profiling
- Bundle size analysis
- Performance regression detection
- Monorepo version management:
- Changesets integration for versioning
- Automated changelog generation
- Coordinated package releases
- Dependency version synchronization
- Interactive CLI tool to customize the template:
- Linter/Formatter options: Choose between Biome, ESLint/Prettier, or other tools
- Error tracking: Optionally include or exclude Sentry
- Authentication: Choose between Supabase Auth, Better Auth, or other providers
- Database: Select Supabase, regular PostgreSQL, or other database solutions
- Additional features: Selectively add Storybook, testing frameworks, etc.