This article was originally published on AI Study Room. For the full version with working code examples and related articles, visit the original post.
Monorepo Setup Guide: Turborepo + pnpm + TypeScript in 30 Minutes
A monorepo lets you share code between apps (web, mobile, docs) and packages (shared utils, configs, UI components) in a single repository. Turborepo + pnpm + TypeScript is the modern stack. Here's how to set it up in 30 minutes.
Why a Monorepo?
| Problem | Monorepo Solution |
|---|---|
| Duplicate tsconfig, ESLint config, etc. in 5 repos | One shared config package. Update once, all apps get it. |
| Copy-pasting UI components between apps | Shared UI package. One component, used everywhere. |
| Can't refactor across apps safely | TypeScript validates ALL consumers when you change a shared package. |
| CI runs unrelated changes on every commit | Turborepo caches tasks. Only changed packages rebuild. |
Step-by-Step Setup
# 1. Create the monorepo structure
mkdir my-monorepo && cd my-monorepo
pnpm init
2. Create pnpm-workspace.yaml
cat > pnpm-workspace.yaml << 'EOF'
packages:
- "apps/*"
- "packages/*"
EOF
3. Create directory structure
mkdir -p apps/web apps/docs packages/ui packages/config
4. Create root package.json with Turborepo
cat > package.json << 'EOF'
{
"private": true,
"scripts": {
"dev": "turbo dev",
"build": "turbo build",
"lint": "turbo lint",
"test": "turbo test"
},
"devDependencies": {
"turbo": "^2.0.0",
"typescript": "^5.5.0"
}
}
EOF
5. Install Turborepo
pnpm install
6. Create turbo.json
cat > turbo.json << 'EOF'
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/", "dist/"]
},
"dev": { "cache": false, "persistent": true },
"lint": { "dependsOn": ["^build"] },
"test": { "dependsOn": ["^build"] }
}
}
EOF
Shared Config Package
# packages/config/package.json
{
"name": "@repo/config",
"version": "0.0.0",
"private": true,
"exports": {
"./typescript": "./tsconfig.base.json",
"./eslint": "./eslint.base.js"
}
}
packages/config/tsconfig.base.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
App Configuration
# apps/web/tsconfig.json — each app extends the shared base
{
"extends": "@repo/config/typescript",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/": ["./src/"],
"@repo/ui/": ["../../packages/ui/src/"]
}
},
"include": ["src", "next-env.d.ts"]
}
Best Practices
- One package = one purpose. @repo/ui for components, @repo/config for shared configs, @repo/utils for shared utilities. Don't create a "misc" package.
-
Use workspace protocol: In package.json dependencies, use
"@repo/ui": "workspace:*"instead of version numbers. - Parallel builds: Turborepo runs independent tasks in parallel. A build across 5 packages finishes in the time of the slowest one, not the sum.
- Remote caching: Turborepo can cache builds remotely (Vercel). CI builds reuse cache from previous CI runs.
- Don't go monorepo for <3 packages. The overhead isn't worth it for tiny projects. Start with a single repo, extract when you have sharing pain.
Bottom line: Monorepos shine when you have 3+ apps/packages that share code. pnpm workspaces + Turborepo is the best stack in 2026. The shared config package alone saves hours of boilerplate setup per new project. See also: Package Manager Comparison and Build Tools Comparison.
Read the full article on AI Study Room for complete code examples, comparison tables, and related resources.
Found this useful? Check out more developer guides and tool comparisons on AI Study Room.
Top comments (0)