Key Takeaways
- A monorepo puts multiple projects in one Git repository — enabling shared code, coordinated releases, and unified tooling
- Monorepos shine when multiple apps share code (UI components, utility libraries, TypeScript types)
- Turborepo and Nx are the two primary JavaScript monorepo tools — Turborepo is simpler, Nx is more feature-rich
- Build caching is the key performance feature: tasks with unchanged inputs are replayed from cache instead of rerun
- Don't use a monorepo just because it sounds advanced — start with a monorepo only when you have shared code across apps
What Is a Monorepo?
A monorepo is a single Git repository that contains multiple projects, packages, or applications. Instead of separate repositories for your web app, mobile app, API, and shared UI library, they all live together in one repo.
Large companies including Google, Meta, Microsoft, and Twitter have used monorepos for years. The approach has advantages for teams who need to share code across projects and coordinate releases. Modern tooling — especially Turborepo and Nx — has made monorepos practical for smaller teams too.
A typical JavaScript monorepo structure:
my-monorepo/
├── apps/
│ ├── web/ # Next.js web app
│ ├── mobile/ # Expo mobile app
│ └── api/ # Express API
├── packages/
│ ├── ui/ # Shared React components
│ ├── utils/ # Shared utility functions
│ └── types/ # Shared TypeScript types
├── turbo.json # Turborepo configuration
└── package.json # Root package.json with workspaces
Monorepo vs Polyrepo
Learn the Core Concepts
Start with the fundamentals before touching tools. Understanding why something was built the way it was makes every tool decision faster and more defensible.
Build Something Real
The fastest way to learn is to build a project that produces a real output — something you can show, share, or deploy. Toy examples teach you the happy path; real projects teach you everything else.
Know the Trade-offs
Every technology choice is a trade-off. The engineers who advance fastest are the ones who can articulate clearly why they chose one approach over another — not just "I used it before."
Go to Production
Development is the easy part. The real learning happens when you deploy, monitor, debug, and scale. Plan for production from day one.
The alternative to a monorepo is a polyrepo — separate repositories for each project. Both approaches have genuine tradeoffs.
Monorepo advantages
- Single source of truth for shared code — change a shared UI component once; all apps using it get the update immediately
- Atomic commits across projects — one commit can update the API and the types package it uses, keeping them in sync
- Unified tooling — one ESLint config, one Prettier config, one TypeScript config for all projects
- Easier cross-project refactoring — rename a function across 5 packages in one PR
Polyrepo advantages
- Simpler CI/CD per project — each repo has independent deployment pipelines
- Team autonomy — different teams can move at different speeds without coordination overhead
- Smaller clone size — checking out one project doesn't download all projects
- Easier access control — different security permissions per repo
When to Use a Monorepo
A monorepo makes sense when:
- You have shared code (UI components, utility libraries, types) used by multiple apps
- Multiple apps need to be deployed in coordination — API types must match what the frontend expects
- Your team is small enough that a single repo doesn't create organizational friction
- You're building a design system or component library that multiple products consume
Skip the monorepo if:
- Your projects don't share code — a monorepo is overhead with no benefit
- Projects are in different languages that don't share tooling
- Independent deployment timelines matter more than coordination
- You're a solo developer with two simple projects
Turborepo: Setup and Basics
Turborepo is the most popular JavaScript monorepo tool as of 2026. It's maintained by Vercel and integrates naturally with Next.js, though it works with any JavaScript project.
Create a new Turborepo
npx create-turbo@latest my-monorepo
cd my-monorepo
This scaffolds a monorepo with a apps/ folder, packages/ folder, and Turborepo configured out of the box.
The turbo.json file
This file defines your task pipeline — how tasks depend on each other and what to cache:
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": []
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
}
}
}
The "dependsOn": ["^build"] means "run the build task in all dependencies first." The caret (^) means "in dependent packages." This ensures your shared packages are built before the apps that use them.
Running tasks
# Build all packages and apps
npx turbo build
# Run tests in all packages
npx turbo test
# Start all dev servers
npx turbo dev
Build Caching Explained
Caching is the most important Turborepo feature. Here's how it works:
- Turborepo hashes all inputs for a task: source files,
package.json, environment variables, task dependencies - If the hash matches a previous run, Turborepo replays the output from cache instead of running the task again
- If inputs changed, the task runs fresh and the new output is cached
Local cache
By default, cache is stored in .turbo/ locally. If you run turbo build and nothing changed, it completes in milliseconds instead of minutes.
Remote cache
Remote caching lets multiple developers and CI servers share the same cache. Enable it with Vercel's remote cache (free for open source, paid for teams) or self-host with a compatible cache store:
# Link to Vercel remote cache
npx turbo login
npx turbo link
With remote caching, if a teammate already built a package with the same inputs, your CI run skips building it and restores from cache. This can cut CI times by 80-90% on large monorepos.
Workspace Packages
Monorepos use npm/yarn/pnpm workspaces to link packages together without publishing to npm. In your root package.json:
{
"name": "my-monorepo",
"private": true,
"workspaces": ["apps/*", "packages/*"]
}
Each package in packages/ is a standard npm package with its own package.json. Apps can import from them:
// In apps/web/package.json
{
"dependencies": {
"@my-monorepo/ui": "*",
"@my-monorepo/utils": "*"
}
}
// In apps/web/src/app/page.tsx
import { Button } from '@my-monorepo/ui'
import { formatDate } from '@my-monorepo/utils'
The workspace symlinks these directly to the source code. Changes to the shared package are immediately reflected in the apps — no publishing step required during development.
CI/CD with a Monorepo
Monorepos need CI configuration that handles affected packages — only running tests and builds for packages that changed.
GitHub Actions with Turborepo
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install
- name: Build and Test
run: npx turbo build test lint
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
The TURBO_TOKEN enables remote caching. On subsequent CI runs where inputs haven't changed, tasks are restored from cache.
Turborepo vs Nx
Both tools manage JavaScript monorepos with build caching and task orchestration. The choice depends on your needs:
Turborepo
- Simpler to set up and understand
- Better Next.js integration (both maintained by Vercel)
- Less opinionated — bring your own generators and structure
- Good choice for: 2-20 packages, small teams, Next.js heavy projects
Nx
- More powerful dependency graph visualization
- Built-in code generators for React, Angular, Node, and more
- Better tooling for large enterprise monorepos with many teams
- Nx Cloud for distributed caching and task distribution
- Good choice for: 20+ packages, multiple teams, complex dependency graphs, Angular projects
Learn Advanced Development Workflows at Our AI Bootcamp
We cover modern tooling, deployment, AI integration, and architecture patterns — hands-on over 3 days for working developers.
Upcoming bootcamps: Denver • New York City • Dallas • Los Angeles • Chicago
View Dates and EnrollFAQ
What is a monorepo?
A monorepo (monolithic repository) is a single Git repository that contains multiple projects or packages. Instead of having separate repos for your frontend, backend, and shared libraries, they all live in one repo. Teams can share code, coordinate changes, and run consistent tooling across all projects.
Turborepo vs Nx — which is better?
Both are excellent. Turborepo (by Vercel) is simpler to set up and focuses on fast build caching. Nx (by Nrwl) is more opinionated, has more built-in generators and tooling, and handles larger, more complex monorepos with many teams. For most JavaScript projects under 50 packages, Turborepo is easier to start with. For large enterprise monorepos with many teams and complex dependency graphs, Nx's extra features are worth the complexity.
When should you NOT use a monorepo?
Avoid monorepos when: projects are in completely different languages with no shared code, teams need completely independent deployment pipelines and governance, the projects have fundamentally different security requirements, or you're a solo developer with a few simple projects that don't share code.
How does Turborepo caching work?
Turborepo hashes the inputs to each task (source files, dependencies, environment variables) and stores the output. If the inputs haven't changed since the last run, Turborepo replays the cached output instead of rerunning the task. This makes subsequent CI runs dramatically faster. Remote caching lets multiple team members and CI servers share the same cache.