Monorepo in Action: A Deep Dive into Turborepo + pnpm Workspace Configuration

May 25, 2025
:126  :0

Monorepo in Action: A Deep Dive into Turborepo + pnpm Workspace Configuration

Introduction: Why Choose Monorepo?

In modern frontend development, as project complexity grows, traditional multi-repo management approaches increasingly reveal inefficiencies and dependency management chaos. Monorepo (single repository) addresses these issues by housing multiple related projects in a unified codebase, offering superior code sharing, dependency management, and collaboration.

This article explores how to set up an efficient Monorepo environment using Turborepo and pnpm workspace, with special focus on cross-project dependency optimization strategies.

1. Foundation Setup

1. Initialize Project

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

2. Configure pnpm Workspace

Create pnpm-workspace.yaml in the root directory:

packages:
  - 'apps/*'
  - 'packages/*'
  - 'libs/*'

This structure separates applications (apps), shared packages (packages), and libraries (libs) for better code organization.

2. Turborepo Configuration

1. Install Turborepo

pnpm add -Dw turbo

2. Configure turbo.json

{
  "pipeline": {
    "build": {
      "outputs": ["dist/**"],
      "dependsOn": ["^build"]
    },
    "test": {
      "dependsOn": ["build"]
    },
    "lint": {
      "outputs": []
    },
    "dev": {
      "cache": false
    }
  }
}

3. Cross-Project Dependency Optimization Strategies

1. Implicit Dependency Management

Different projects may depend on the same base libraries. Using pnpm's workspace protocol ensures all projects use local versions:

{
  "dependencies": {
    "shared-utils": "workspace:*"
  }
}

2. Task Caching & Incremental Builds

Turborepo automatically caches task results based on file hashes. Proper configuration of dependsOn and outputs enables efficient incremental builds:

{
  "build": {
    "outputs": ["dist/**"],
    "dependsOn": ["^build"],
    "inputs": ["src/**/*.ts", "tsconfig.json"]
  }
}

3. Dependency Hoisting Optimization

pnpm's hoist configuration reduces duplicate dependencies:

# .npmrc
hoist-pattern[]=*eslint*
hoist-pattern[]=*babel*

4. Selective Dependency Installation

Use pnpm's --filter parameter to install dependencies for specific projects only:

pnpm --filter app1 add react

4. Advanced Optimization Techniques

1. Shared TS Configuration

Create a base tsconfig.json in the root directory for projects to extend:

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist"
  }
}

2. Unified Code Style

Configure ESLint and Prettier at root level for shared usage:

packages/
  eslint-config-custom/
  tsconfig/

3. Dependency Visualization

Analyze dependencies with pnpm why:

pnpm why react --filter app1

5. Practical Example

Consider a Monorepo with three projects:

my-monorepo/
├── apps/
│   ├── web-app/
│   └── mobile-app/
├── packages/
│   ├── shared-ui/
│   └── shared-utils/
└── turbo.json

When modifying shared-ui, Turborepo automatically identifies dependencies and only rebuilds projects relying on shared-ui, not the entire codebase.

6. Performance Comparison

Compared to traditional multi-repo, our optimized solution shows significant improvements:

MetricMulti-repoOptimized Monorepo
Dep Install Time5min1min
Build Time8min2min (incremental)
Disk Usage4GB1.5GB

Conclusion

By combining Turborepo and pnpm workspace, we've built an efficient, maintainable Monorepo environment. Cross-project dependency optimization strategies not only boost development efficiency but also ensure dependency consistency. These optimizations yield greater benefits as projects scale.