Introduction
In modern software development, codebases continue to grow in both size and complexity. Teams often maintain multiple applications, shared libraries, and internal tools that must stay aligned. This is one of the main reasons monorepos have become increasingly popular—they streamline development, improve code sharing, and reduce dependency-related issues.
When I first built three React.js applications for a client, each project relied on the same set of core libraries. Because I wasn’t familiar with monorepos at the time, I created three separate repositories. This turned into a maintenance headache: whenever the client updated the core library, I had to apply the change in all three apps and test each one individually. The process was slow, repetitive, and error-prone. Looking back, a monorepo structure would have eliminated most of that duplicated effort and made the entire workflow far more efficient.
In this blog, you’ll learn:
- What a monorepo is
- Benefits compared to a traditional (polyrepo) structure
- Why you should use one
- A step-by-step guide to setting up a monorepo using Turborepo, PNPM, Next.js, and Playwright
What Is a Monorepo?
A monorepo (monolithic repository) is a single version-controlled repository that contains multiple projects—such as applications, packages, utilities, shared components, and infrastructure code.
Examples:
- Google, Meta, Microsoft, and Uber use monorepos internally.
- Many modern frontend ecosystems (like Vercel) recommend monorepos for multi-app structures.
Monorepos can contain:
- Multiple frontend apps (Next.js, React)
- Backend services (Node.js, .NET, Go)
- Shared UI libraries
- Shared utilities
- Documentation
- Configuration files (ESLint, Typescript, Prettier)
All projects live together in one place, managed by a unified toolchain.
Monorepo vs Polyrepo (Normal Repository)
Polyrepo (Traditional)
Each project lives in its own repository.
Pros:
- Simpler for small projects
- Clear boundaries
- Independent versioning
Cons:
- Harder to share code
- Duplicate configurations in each repo
- Dependency management becomes inconsistent
- Harder to ensure unified coding standards
- Many repo contexts → switching costs
Monorepo
All related projects in a single repository.
Pros:
- Easy code sharing (UI components, utils)
- Single place for dependencies → no version drift
- Unified tooling (ESLint, TSConfig, Prettier)
- Atomic commits across multiple packages
- Simplified collaboration
- Better CI performance with incremental builds (Turborepo)
Cons:
- Requires tooling to manage effectively
- Without good structure, can get messy
Why Use a Monorepo?
You want a monorepo if:
- Your project has multiple apps that share common code
- Teams work across multiple services and UI libraries
- You want consistent tooling and faster development
- You want powerful caching, shared dependencies, and improved CI/CD
Especially with frameworks like Next.js, monorepos make code sharing extremely smooth.
What Is Turborepo?
Turborepo is a high-performance build system for JavaScript/TypeScript monorepos.
It helps you run tasks (build, lint, test, type-check…) faster and smarter, especially when you have many apps and shared packages.
Remote & Local Caching (the magic part)
Caching is Turborepo’s biggest strength.
Whenever you run a task (e.g., build, test):
-
Turborepo saves the output
-
And most importantly, the inputs (source code, config, env, lockfile)
The next time you run the same task with unchanged inputs, it skips the entire execution and returns the cached result instantly.
✔ Example
Suppose you have:
apps/
app1
app2
app3
packages/
ui
utils
If you build all three apps:
turbo build
Then only change code inside app1 and run again:
turbo build
Turborepo:
-
Rebuilds only app1
-
Returns cached results for app2 and app3
-
Skips tests for apps whose code didn’t change
-
Skips builds for shared packages that were unchanged
This can turn a 3-minute build into a 10-second operation.
How to Build a Monorepo Using Turborepo, PNPM, Next.js & Playwright
1. Install PNPM
PNPM is fast, disk-efficient, and works great for monorepos.
npm install -g pnpm
2. Create a Turborepo
pnpm create turbo@latest my-monorepo
cd my-monorepo
Turborepo will generate a structure:
my-monorepo
├─ apps/
├─ packages/
├─ turbo.json
├─ package.json
3. Add a Next.js Application
Inside apps/:
cd apps
pnpm create next-app@latest web
Install dependencies at the root (workspace):
pnpm install
Add this to the root package.json:
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
]
}
4. Create a Shared UI Package
Inside packages/:
mkdir ui
cd ui
pnpm init -y
Add React dependency:
pnpm add react react-dom -w
Example component:
// packages/ui/Button.tsx
export const Button = () => <button>Shared Button</button>
Use in Next.js app:
import { Button } from "ui/Button";
5. Configure Turborepo Pipelines
In turbo.json:
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"dev": {
"cache": false
}
}
}
6. Add Playwright to the Monorepo
Install Playwright
pnpm dlx playwright@latest install
Set up Playwright in apps/web:
cd apps/web
pnpm exec playwright init
Using Playwright in a Monorepo
You can:
- Run E2E tests per application
- Share test utilities in
packages/test-utils
Add script in root package.json:
"scripts": {
"test:e2e": "turbo run test:e2e"
}
7. Running Everything
Start Dev Mode
pnpm dev
Turborepo runs dev servers for all apps.
Run Build
pnpm build
Utilizes Turborepo caching for faster builds.
Run Playwright
pnpm test:e2e
Conclusion
A monorepo is a powerful way to structure modern applications—especially when you need to share code across multiple apps and packages. By combining Turborepo, PNPM, Next.js, and Playwright, you get:
- Unified development workflows
- Faster builds with caching
- Shared UI libraries and utilities
- Scalable testing setup
Whether you’re building an e-commerce platform, an enterprise dashboard suite, or internal tools, a monorepo can significantly streamline development and collaboration.
If you’re starting a new large-scale project today, a monorepo is absolutely worth considering.