原始内容
Vercel + Expo App Template
A production-ready monorepo template for building full-stack applications with Next.js, Expo (optional), and a curated set of integrations.
Stack
| Category | Technology |
|---|---|
| Framework | Next.js 15 (React 19) |
| Mobile | Expo (React Native) - optional |
| Database | Neon (Serverless PostgreSQL) + Drizzle ORM |
| Auth | Clerk |
| Payments | Stripe |
| Analytics | PostHog |
| Monitoring | Sentry |
| API | tRPC |
| i18n | next-intl (web), use-intl (mobile) |
| State | Zustand |
| Styling | Tailwind CSS |
| Package Manager | pnpm + Turborepo |
Quick Start
Creating a New App
Option 1: Use GitHub's template feature
Click the green "Use this template" button above, or click here.
Then:
git clone https://github.com/YOUR_USERNAME/my-app.git
cd my-app
./scripts/setup.sh my-app
./scripts/provision.sh
pnpm dev
Option 2: Clone directly
git clone https://github.com/gmackie/vercel-expo-app-template.git my-app
cd my-app
./scripts/setup.sh my-app
./scripts/provision.sh
pnpm dev
Option 2: Use the create script
./scripts/create-app.sh my-app
Option 3: Web-only (no mobile)
./scripts/setup.sh my-app --no-mobile
Project Structure
├── apps/
│ ├── web/ # Next.js web application
│ │ ├── src/
│ │ │ ├── app/ # App router pages
│ │ │ └── lib/ # Client utilities
│ │ └── package.json
│ │
│ └── mobile/ # Expo mobile app (optional)
│ ├── src/
│ └── package.json
│
├── packages/
│ ├── api/ # tRPC routers and procedures
│ ├── db/ # Drizzle schema and client
│ ├── shared/ # Shared types, utils, validators
│ ├── analytics/ # PostHog wrapper (web + native)
│ ├── monitoring/ # Sentry wrapper (web + native)
│ ├── payments/ # Stripe wrapper
│ ├── i18n/ # Internationalization (next-intl + use-intl)
│ └── store/ # Zustand state management
│
├── tooling/
│ ├── eslint/ # Shared ESLint configs
│ └── typescript/ # Shared TypeScript configs
│
└── scripts/
├── setup.sh # Rename template to your app
├── provision.sh # Auto-provision external services
└── create-app.sh # All-in-one app creation
Scripts
./scripts/setup.sh
Renames the template to your app name and configures the monorepo.
# Basic usage
./scripts/setup.sh my-app
# Without mobile app
./scripts/setup.sh my-app --no-mobile
What it does:
- Renames all
@repo/*packages to@my-app/* - Updates app titles and metadata
- Creates
.env.localfrom.env.example - Reinstalls dependencies
- Initializes a fresh git repository
./scripts/provision.sh
Interactively provisions external services and saves credentials to .env.local.
# Full provisioning
./scripts/provision.sh
# Neon only
./scripts/provision.sh --neon-only
# Skip specific services
./scripts/provision.sh --skip-clerk --skip-sentry
Supported services:
- Neon - Automatically creates database via CLI (
npm i -g neonctl) - Clerk - Manual key entry (dashboard required)
- Stripe - Manual key entry
- PostHog - Manual key entry
- Sentry - Manual key entry
./scripts/create-app.sh
All-in-one script that clones, sets up, and optionally provisions.
./scripts/create-app.sh my-app
./scripts/create-app.sh my-app --no-mobile
./scripts/create-app.sh my-app --no-provision
Development
Web Only
# Start web app only
pnpm dev:web
Mobile Development
The mobile app has three environments:
| Environment | Command | Description |
|---|---|---|
| Local | pnpm dev:mobile |
Uses ngrok tunnel, works with Expo Go |
| Beta | pnpm eas:dev |
EAS build with internal distribution |
| Production | pnpm eas:prod |
EAS build for App Store / Play Store |
Local Development
# Start full mobile dev environment (Next.js + ngrok + Expo)
pnpm dev:mobile
# With a static ngrok domain (requires paid ngrok)
./scripts/dev-mobile.sh --domain myapp
# Use Expo Go instead of dev client
./scripts/dev-mobile.sh --expo-go
Prerequisites:
- ngrok:
brew install ngrok - ngrok account: https://dashboard.ngrok.com (free tier works)
- jq:
brew install jq
Beta / Internal Testing
Build a development client for testing on real devices without Expo Go:
# Build for iOS (ad-hoc signed for registered devices)
pnpm eas:dev --platform ios
# Build for Android (debug APK)
pnpm eas:dev --platform android
# Build for both
pnpm eas:dev --platform all
This creates a separate app (with .dev suffix on bundle ID) that can be installed alongside the production app.
Register test devices:
eas device:create
Production Build
# Build for App Store / Play Store
pnpm eas:prod --platform all
# Submit to stores
eas submit --platform ios
eas submit --platform android
All Apps
# Start all apps in development mode
pnpm dev
# Type check all packages
pnpm typecheck
# Lint all packages
pnpm lint
# Build all packages
pnpm build
Database
This template uses Neon - serverless PostgreSQL with autoscaling, branching, and a generous free tier.
# Push schema changes to Neon
pnpm db:push
# Open Drizzle Studio
pnpm db:studio
# Generate migrations
pnpm --filter @my-app/db db:generate
Setting up Neon
Option 1: Using the CLI (recommended)
# Install the Neon CLI
npm install -g neonctl
# Authenticate
neonctl auth
# Run provisioning script
./scripts/provision.sh --neon-only
Option 2: Manual setup
- Go to console.neon.tech
- Create a new project
- Copy the connection string to
.env.local
Scaling Beyond Neon
Neon's serverless PostgreSQL is excellent for most applications and scales well into production workloads. However, if you need:
- Horizontal sharding for massive write throughput
- Global edge deployment with single-digit millisecond latency worldwide
- Vitess-powered MySQL for proven hyperscale infrastructure
Consider migrating to PlanetScale. The migration path is straightforward:
- Export your Drizzle schema
- Update
packages/dbto usedrizzle-orm/planetscale-serverless - Update
DATABASE_URLto your PlanetScale connection string - Run migrations
Most apps will never need this - Neon handles significant scale with its autoscaling and read replicas.
Environment Variables
Copy .env.example to .env.local and fill in your values:
# Neon Database (PostgreSQL)
DATABASE_URL=postgresql://user:password@ep-xxx.region.aws.neon.tech/dbname?sslmode=require
# Clerk Authentication
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
# Stripe Payments
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# PostHog Analytics
NEXT_PUBLIC_POSTHOG_KEY=phc_...
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
# Sentry Monitoring
SENTRY_DSN=https://...@sentry.io/...
SENTRY_AUTH_TOKEN=sntrys_...
Deployment
Vercel (Recommended)
- Push to GitHub/GitLab
- Import to Vercel
- Set environment variables
- Deploy
The vercel.json is pre-configured for the web app.
Manual
pnpm build
pnpm start
Adding New Packages
# Create a new package
mkdir packages/my-package
cd packages/my-package
# Initialize with the shared config
cat > package.json << EOF
{
"name": "@my-app/my-package",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
}
},
"scripts": {
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@my-app/typescript-config": "workspace:*",
"typescript": "^5.7.2"
}
}
EOF
# Add tsconfig
cat > tsconfig.json << EOF
{
"extends": "@my-app/typescript-config/react-library.json",
"include": ["src"],
"exclude": ["node_modules"]
}
EOF
mkdir src
echo 'export const hello = "world";' > src/index.ts
# Install dependencies
cd ../..
pnpm install
License
MIT