A modern, production-ready monorepo template for building universal apps with React Native, Expo, and Next.js.
Quick Start β’ Documentation β’ Scripts β’ Architecture
- π Quick Start
- π Project Structure
- π§ Available Scripts
- π Documentation
- ποΈ Architecture
- π§ͺ CI/CD Pipeline
- π€ Contributing
- π License
- Node.js 20+ or 22 (LTS recommended) (Download)
- Corepack enabled (included with Node.js 16.9+)
-
Clone the repository
git clone <your-repo-url> cd expo-react-native-next-starter
-
Enable Corepack (if not already enabled)
corepack enableThis ensures you're using the correct Yarn version (4.5.0) specified in
package.json. -
Install dependencies
yarn install
-
Start developing!
# Start all apps in watch mode yarn watch # OR start individually: # Web development (Next.js) yarn web # Mobile development (Expo) yarn native # Storybook yarn storybook
-
Open the apps
- Web: http://localhost:3000
- Native: Scan QR code with Expo Go app
- Storybook: http://localhost:6006
.
βββ apps/
β βββ expo/ # π± React Native mobile app
β β βββ app/ # Expo Router screens
β β βββ babel.config.js
β β βββ package.json
β βββ next/ # π Next.js web application
β β βββ app/ # App Router pages
β β βββ next.config.js
β β βββ package.json
β βββ storybook/ # π Component documentation
β βββ stories/
β βββ package.json
βββ packages/
β βββ ui/ # π¨ Shared UI components
β β βββ src/
β βββ app/ # π¦ Shared app logic and screens
β β βββ features/ # Feature-based organization
β β βββ provider/ # Context providers
β βββ api/ # π API client and types
β β βββ src/
β βββ config/ # βοΈ Shared configuration
β βββ src/ # Theme, colors, constants
βββ .github/
β βββ workflows/ # CI/CD pipelines
βββ biome.json # Linting & formatting config
βββ turbo.json # Turbo build configuration
βββ package.json # Root workspace config
βββ tsconfig.base.json # Base TypeScript config
| Package | Description | Used By |
|---|---|---|
@my/ui |
Reusable UI components (Button, Text, etc.) | All apps |
app |
Shared screens, features, and navigation logic | Expo, Next.js |
@my/api |
API client, types, and data fetching | All apps |
@my/config |
Theme, colors, spacing, constants | All packages |
| Command | Description |
|---|---|
yarn native |
Start Expo development server |
yarn native:prebuild |
Generate native iOS/Android projects |
yarn ios |
Run on iOS simulator |
yarn android |
Run on Android emulator |
yarn web |
Build packages and start Next.js dev server |
yarn web:prod |
Build Next.js for production |
yarn web:prod:serve |
Serve production build locally |
yarn storybook |
Start Storybook development server |
yarn watch |
Start all apps in watch mode (parallel) |
| Command | Description |
|---|---|
yarn typecheck |
Run TypeScript type checking across all workspaces |
yarn test |
Run all tests with Vitest |
yarn test:watch |
Run tests in watch mode for development |
yarn lint |
Check code quality with Biome |
yarn lint:fix |
Auto-fix linting and formatting issues |
| Command | Description |
|---|---|
yarn build |
Build all packages (excludes Next.js app) |
yarn clean |
Remove all node_modules and reinstall |
yarn lint-sherif |
Check workspace dependency consistency |
-
Add component to
packages/ui/src/// packages/ui/src/MyButton.tsx import { Pressable, Text } from 'react-native' export function MyButton({ title, onPress }) { return ( <Pressable onPress={onPress}> <Text>{title}</Text> </Pressable> ) }
-
Export from
packages/ui/src/index.tsxexport * from './MyButton'
-
Use in any app
import { MyButton } from '@my/ui'
-
Add screen to
packages/app/features/// packages/app/features/profile/screen.tsx export function ProfileScreen() { return ( <View> <Text>Profile Screen</Text> </View> ) }
-
Add to Expo Router
// apps/expo/app/profile.tsx import { ProfileScreen } from 'app/features/profile/screen' export default ProfileScreen
-
Add to Next.js
// apps/next/app/profile/page.tsx import { ProfileScreen } from 'app/features/profile/screen' export default function ProfilePage() { return <ProfileScreen /> }
This template uses Vitest for fast unit testing with a Jest-compatible API.
# Run all tests once
yarn test
# Run tests in watch mode (for development)
yarn test:watch
# Run tests in a specific workspace
cd packages/ui && yarn testTests are co-located with source files using .test.ts or .test.tsx extensions:
// packages/ui/src/Button.test.tsx
import { render, fireEvent } from '@testing-library/react-native'
import { Button } from './Button'
describe('Button', () => {
it('calls onPress when pressed', () => {
const onPress = vi.fn()
const { getByText } = render(
<Button title="Click me" onPress={onPress} />
)
fireEvent.press(getByText('Click me'))
expect(onPress).toHaveBeenCalledTimes(1)
})
})apps/next/lib/utils.test.ts- Utility function testsapps/next/app/layout.test.tsx- Metadata testspackages/ui/src/Button.test.tsx- Component testspackages/app/features/home/screen.test.tsx- Screen component tests
This template maintains high code quality through automated checks.
TypeScript type safety is enforced across all workspaces without suppressing errors.
# Type check all workspaces
yarn typecheck
# Type check a specific workspace
cd apps/next && yarn typecheckConfiguration:
- Type checking is enabled in all workspaces
- Test files are excluded from builds but still type-checked
- The Next.js build will fail on TypeScript errors
This template uses Biome - a fast, all-in-one toolchain that replaces ESLint and Prettier.
# Check for issues
yarn lint
# Auto-fix issues
yarn lint:fixPre-commit Hooks:
- Automatically formats and lints staged files
- Runs via Husky + lint-staged
- Ensures code quality before commits
This template includes the React Compiler (formerly React Forget) - an experimental feature that automatically optimizes your React components.
The React Compiler automatically memoizes components and values, eliminating the need for manual optimization with useMemo, useCallback, and memo. It analyzes your code at build time and adds optimizations automatically.
β
Automatic Memoization - No more manual useMemo/useCallback
β
Better Performance - Fewer re-renders out of the box
β
Cleaner Code - Write idiomatic React without optimization clutter
β
Type-Safe - Works seamlessly with TypeScript
React Compiler is already configured for both apps:
Expo (Babel):
// apps/expo/babel.config.js
plugins: [
['babel-plugin-react-compiler', {
target: '18' // React Native compatibility
}],
]Next.js:
// apps/next/next.config.js
experimental: {
reactCompiler: true
}Just write normal React code - the compiler handles optimization:
// β
Before: Manual optimization
const MemoizedComponent = memo(function MyComponent({ data }) {
const processed = useMemo(() => processData(data), [data])
const handleClick = useCallback(() => {
console.log(processed)
}, [processed])
return <Button onPress={handleClick} />
})
// β
After: Let the compiler handle it
function MyComponent({ data }) {
const processed = processData(data)
const handleClick = () => {
console.log(processed)
}
return <Button onPress={handleClick} />
}Edit the shared configuration in packages/config/src/index.ts:
export const theme = {
colors: {
primary: '#007AFF',
secondary: '#5856D6',
background: '#FFFFFF',
text: '#000000',
},
spacing: {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
},
borderRadius: {
sm: 4,
md: 8,
lg: 12,
full: 9999,
},
}Configure API endpoints in packages/api/src/client.ts:
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'https://api.example.com'
export const apiClient = {
get: async (endpoint: string) => {
const response = await fetch(`${API_BASE_URL}${endpoint}`)
return response.json()
},
// ... other methods
}Create .env.local files in each app:
# apps/next/.env.local
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_APP_NAME=My App
# apps/expo/.env.local (using expo-constants)
EXPO_PUBLIC_API_URL=https://api.example.comVercel (Recommended):
# Install Vercel CLI
npm i -g vercel
# Deploy
vercelBuild for Production:
yarn web:prodThe build output will be in apps/next/.next/.
Environment Variables:
Set environment variables in your hosting platform's dashboard or .env.production.
Development Build:
# Generate native projects
yarn native:prebuild
# iOS
yarn ios
# Android
yarn androidProduction Build with EAS:
# Install EAS CLI
npm install -g eas-cli
# Configure EAS
cd apps/expo
eas build:configure
# Build for iOS
eas build --platform ios
# Build for Android
eas build --platform android
# Submit to stores
eas submit --platform ios
eas submit --platform androidLearn more:
This template uses Yarn Workspaces + Turbo for efficient monorepo management:
Root (yarn workspace)
βββ apps/* # Platform-specific applications
β βββ expo # React Native mobile
β βββ next # Next.js web
β βββ storybook # Component docs
βββ packages/* # Shared code
βββ ui # UI components
βββ app # App logic
βββ api # API client
βββ config # Configuration
βββββββββββββββββββββββββββββββββββββββββββ
β Apps (Platform-Specific Entry Points) β
βββββββββββββββββββββββββββββββββββββββββββ
β
βββ> apps/expo (React Native)
βββ> apps/next (Next.js)
βββ> apps/storybook
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β Shared Packages β
βββββββββββββββββββββββββββββββββββββββββββ
β
βββ> app (screens & features)
βββ> @my/ui (components)
βββ> @my/api (data layer)
βββ> @my/config (theme)
- Universal First: Write once, run everywhere
- Type Safety: TypeScript everywhere
- Modular: Small, focused packages
- Fast Builds: Turbo caching + parallel execution
- Developer Experience: Fast feedback loops
Turbo orchestrates builds with intelligent caching:
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"typecheck": {
"dependsOn": ["^build"]
}
}
}Benefits:
- β Builds packages in correct order
- β Caches unchanged packages
- β Runs tasks in parallel
- β Incremental builds
The GitHub Actions CI pipeline is optimized for speed with parallel execution:
ββββββββββββββββββββββββββββββββββββββββββββββββ
β Stage 1: Quality Checks (Parallel) β
ββββββββββββββββββββ¬ββββββββββββββββββββββββββββ€
β Lint β Type Check β
β β’ Biome β β’ All packages β
β β’ Format check β β’ Next.js app β
β β β’ Expo app β
ββββββββββββββββββββ΄ββββββββββββββββββββββββββββ
β β
ββββββββββ¬ββββββββ
βΌ
ββββββββββββββββββββββ
β Both must pass β
ββββββββββββββββββββββ
β
βββββββββββββ΄βββββββββββ
βΌ βΌ
ββββββββββββββββββββ ββββββββββββββββββββ
β Build β β Test β
β β’ Packages β β β’ Vitest β
β β’ Next.js β β β’ All tests β
β β’ Expo check β β β
ββββββββββββββββββββ ββββββββββββββββββββ
β
Fast Feedback - Quality checks run first and fail fast
β
Efficient - Build and test run in parallel
β
Cached - Dependencies and build artifacts are cached
β
Reliable - Consistent across all environments
# Run all quality checks
yarn lint && yarn typecheck
# Run tests
yarn test
# Run full build
yarn build && yarn web:prodContributions are welcome! Please follow these guidelines:
This project uses Conventional Commits:
# Format: <type>(<scope>): <description>
feat(ui): add new Button variant
fix(api): handle network errors properly
docs: update installation instructions
chore(deps): upgrade React to 19.0Types:
feat: New featurefix: Bug fixdocs: Documentationstyle: Code style (formatting, etc.)refactor: Code refactoringtest: Adding testschore: Maintenance
- Fork & clone the repository
- Create a branch:
git checkout -b feature/my-feature - Make changes with conventional commits
- Run quality checks:
yarn lint && yarn typecheck && yarn test - Push & create a pull request
- Follow the existing code style
- Use TypeScript for type safety
- Write tests for new features
- Update documentation as needed
- Biome handles formatting automatically
MIT License - see LICENSE for details.
Built with β€οΈ using React Native, Expo, and Next.js