Profile picture

@devdaman

React Developer

Back to blog

Monorepos and Turborepos: A Complete Guide and Setup Walkthrough

monorepos and turborepos

Monorepos and Turborepos: A Complete Guide and Setup Walkthrough

As teams scale and applications grow more complex, codebases start to fragment. A frontend lives in one repo, the backend in another, shared utilities in a third, and deployment scripts in a fourth. While this modularity has its perks, it can quickly turn into a mess of dependencies and version mismatches. This is where Monorepos step in, and more recently, Turborepos have taken that idea to the next level.

In this article, we’ll cover:

  • What is a Monorepo?
  • Monorepo vs Polyrepo
  • The problems Monorepos solve
  • What is Turborepo?
  • Benefits of Turborepo
  • How to set up a Turborepo from scratch
  • Common pitfalls and tips

🔍 What is a Monorepo?

Monorepo stands for Monolithic Repository — a software development strategy where multiple projects (packages, apps, libs) live inside a single version-controlled codebase (like a Git repo). These could be frontend apps, backend services, design systems, CLI tools, etc.

Instead of having:

github.com/org/frontend
github.com/org/backend
github.com/org/ui-library

…you’d have one:

github.com/org/monorepo

With a folder structure like:

/apps
/frontend
/backend
/packages
/ui-library
/utils

Each part is still modular — but it’s all in one repo.

🆚 Monorepo vs Polyrepo

Feature Monorepo Polyrepo Repos One shared One per project Tooling Needs monorepo-aware tools Standard tools work fine Dependencies Easier to share Harder to manage shared code CI/CD Smarter with caching (like turbo) Repeated work across repos Scaling Teams Good for tight collaboration Better for completely isolated teams

🤔 Why Monorepo?

Some reasons why monorepos became popular:

  • Shared Code: Easily import internal libraries without npm publishing.
  • Atomic Changes: Update backend and frontend together in one commit.
  • Unified Tooling: One ESLint/TS config, one versioning strategy.
  • Better Developer Experience: pnpm install just works. No version hell.
  • Code Visibility: Everyone sees everything, encouraging cross-team collab.

But managing builds and caching in large monorepos can be hard — that’s where Turborepo comes in.

⚡ What is Turborepo?

Turborepo is a high-performance build system for JavaScript/TypeScript monorepos, created by Vercel.

It introduces a blazing-fast caching system that understands your project’s dependency graph and only rebuilds what’s necessary. It's built for frameworks like Next.js, React, Node.js, and works especially well with pnpm.

Features:

  • Remote & local caching
  • Parallel execution
  • Incremental builds
  • Built-in task pipelines
  • Compatible with pnpm, npm, yarn

🚀 How to Set Up a Monorepo with Turborepo

Let’s go hands-on and build a modern monorepo setup with:

  • pnpm as package manager
  • turbo as build system
  • A frontend app (React)
  • A backend app (Express)
  • A shared ui package

✅ Step 1: Initialize the Monorepo

mkdir my-monorepo && cd my-monorepo
pnpm init

Install turbo:

pnpm add -D turbo

Setup basic folder structure:

mkdir apps packages

✅ Step 2: Create pnpm-workspace.yaml

This tells pnpm to treat all subfolders as part of the same workspace.

# pnpm-workspace.yaml
packages:
- apps/*
- packages/*

✅ Step 3: Add a Frontend App

We’ll use Vite + React:

cd apps
pnpm create vite frontend --template react-ts
cd frontend
pnpm install

✅ Step 4: Add a Backend App

We’ll use Express:

cd ../../apps
mkdir backend && cd backend
pnpm init -y
pnpm add express

Create index.js:

const express = require("express")
const app = express()

app.get("/", (req, res) => res.send("Hello from backend"))
app.listen(3000, () => console.log("Server running on 3000"))

✅ Step 5: Create Shared UI Package

cd ../../packages
mkdir ui && cd ui
pnpm init -y

Add a simple button:

// packages/ui/Button.tsx
import React from "react"

export const Button = () => {
return <button>Shared Button</button>
}

Set up exports in package.json:

{
"name": "ui",
"version": "1.0.0",
"main": "Button.tsx"
}

✅ Step 6: Link the Shared UI in Frontend

cd ../../apps/frontend
pnpm add ui --filter ./packages/ui

Now import your button in the React app.

✅ Step 7: Add Turborepo Configuration

// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false
}
}
}

Each build task depends on its dependencies’ build. You can now run:

pnpm turbo run build

…and only the necessary packages will build. You can add dev scripts for parallel development.

✅ Step 8: Add Scripts

Update package.json in root:

{
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build",
"clean": "turbo run clean"
}
}

And in each app:

// apps/frontend/package.json
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}

// apps/backend/package.json
{
"scripts": {
"dev": "node index.js",
"build": "echo 'No build needed'"
}
}

🧠 Tips and Gotchas

  • Use pnpm for better hoisting and workspace performance
  • Make sure your shared packages don’t import from app-specific code
  • You can use turbo run build --filter=frontend to build just one app
  • Add remote caching (Vercel + GitHub integration) for team setups

📦 Real-World Use Cases

  • Vercel: Turborepo powers their dashboard and open-source tools
  • Shopify: Monorepos for UI and backend services
  • Meta: One giant monorepo with millions of files

🔗 Resources

Here are some great links to go deeper:

  1. Official Turborepo Docs
  2. Monorepo vs Polyrepo - Nx Blog
  3. PNPM Workspaces Guide
  4. Vercel Turborepo Video Intro
  5. Why You Need a Monorepo (by Lee Robinson)

🧾 Final Words

Monorepos simplify collaboration, code sharing, and consistency across projects. With tools like Turborepo and pnpm, managing them becomes fast and scalable. You don’t need to reinvent the wheel — just structure things cleanly, define your pipeline, and let turbo do the heavy lifting.

Contact

devDaman