Back to Architecture & Design Patterns

monorepo-management

monorepoturboreponxpnpmworkspacesbuild systemscalabilitycode sharing
⭐ 36.8kπŸ“„ MITπŸ•’ 2026-06-16Source β†—

Install this skill

npx skills add wshobson/agents

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

Monorepo management centralizes multiple projects and libraries within a single version control repository. By grouping related applications and shared packages under one directory, developers ensure atomic commits that update dependencies and feature code simultaneously. This architecture enforces uniform linting, testing, and formatting standards across all internal modules. Modern tooling like Turborepo and pnpm workspaces enables remote caching and incremental builds, significantly reducing execution time during CI/CD processes. Effective organization involves separating code into distinct 'apps' and 'packages' folders, allowing internal projects to consume local libraries through workspace protocols. This approach removes the overhead of publishing private packages to external registries during development, simplifying local cross-package modifications while keeping project dependencies strictly isolated and versioned. It is a necessary pattern for teams scaling beyond individual application silos.

When to Use This Skill

  • β€’Aligning internal design systems across multiple frontend web applications
  • β€’Synchronizing backend API shared types with frontend consuming clients
  • β€’Managing large-scale migrations by keeping legacy and new codebases in one tree
  • β€’Consolidating shared utility libraries into a single maintainable package

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • β€œSet up a new monorepo using pnpm workspaces
  • β€œHow to configure Turborepo pipelines for incremental builds
  • β€œMigrate multiple separate project repos into one monorepo
  • β€œManage dependencies across my shared UI packages
  • β€œCreate a new workspace package in my existing monorepo

Pro Tips

  • πŸ’‘Always start with pnpm workspaces for superior dependency hoisting, disk space efficiency, and faster installations compared to npm or Yarn.
  • πŸ’‘Leverage Turborepo's remote caching and task orchestration to significantly speed up local and CI/CD builds, especially in large monorepos with many interdependent tasks.
  • πŸ’‘Define clear boundaries and explicit `tsconfig.json` references between packages to enforce proper module isolation and prevent unwanted circular dependencies.

What this skill does

  • β€’Atomic multi-package commits and dependency synchronization
  • β€’Incremental build acceleration using task graph caching
  • β€’Filtering commands to execute scripts across specific workspace sub-projects
  • β€’Centralized configuration for shared TypeScript, linting, and build settings
  • β€’Local package consumption via workspace symlinks to bypass external registry publishing

When not to use it

  • βœ•Small projects with independent release cycles that rarely share internal code
  • βœ•Organizations requiring strictly separated access control per service repository

Example workflow

  1. Initialize root directory with workspace configuration files
  2. Organize project structure into app and package sub-directories
  3. Define build and test dependency graphs in turbo.json
  4. Install local packages into target applications using workspace filtering
  5. Execute cross-package scripts to verify build consistency

Prerequisites

  • –Node.js environment
  • –Basic understanding of package manager workspaces

Pitfalls & limitations

  • !High initial complexity for CI/CD pipeline setup
  • !Potential for circular dependencies between packages
  • !Increased repository clone size and disk usage over time

FAQ

Should I use Turborepo or Nx?
Turborepo is generally lighter and easier to adopt for existing projects, while Nx provides a more opinionated, feature-rich set of tools for enterprise-scale environments.
Why is pnpm preferred over npm for monorepos?
pnpm handles workspace symlinking more efficiently and uses a global content-addressable store to save disk space and speed up installs.
Do I need to publish my packages to npm to share them?
No. Workspace protocols allow your applications to reference local package source code directly without publishing to a registry.

How it compares

Managing monorepos manually requires complex git submodules or scripted cross-repo coordination, whereas specialized tools automate dependency linking and execution order through intelligent caching.

Source & trust

⭐ 37k starsπŸ“„ MITπŸ•’ Updated 2026-06-16
πŸ“„ Full skill instructions β€” original source: wshobson/agents
# Monorepo Management

Build efficient, scalable monorepos that enable code sharing, consistent tooling, and atomic changes across multiple packages and applications.

## When to Use This Skill

- Setting up new monorepo projects
- Migrating from multi-repo to monorepo
- Optimizing build and test performance
- Managing shared dependencies
- Implementing code sharing strategies
- Setting up CI/CD for monorepos
- Versioning and publishing packages
- Debugging monorepo-specific issues

## Core Concepts

### 1. Why Monorepos?

**Advantages:**

- Shared code and dependencies
- Atomic commits across projects
- Consistent tooling and standards
- Easier refactoring
- Simplified dependency management
- Better code visibility

**Challenges:**

- Build performance at scale
- CI/CD complexity
- Access control
- Large Git repository

### 2. Monorepo Tools

**Package Managers:**

- pnpm workspaces (recommended)
- npm workspaces
- Yarn workspaces

**Build Systems:**

- Turborepo (recommended for most)
- Nx (feature-rich, complex)
- Lerna (older, maintenance mode)

## Turborepo Setup

### Initial Setup

# Create new monorepo
npx create-turbo@latest my-monorepo
cd my-monorepo

# Structure:
# apps/
# web/ - Next.js app
# docs/ - Documentation site
# packages/
# ui/ - Shared UI components
# config/ - Shared configurations
# tsconfig/ - Shared TypeScript configs
# turbo.json - Turborepo configuration
# package.json - Root package.json


### Configuration

// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
},
"type-check": {
"dependsOn": ["^build"],
"outputs": []
}
}
}


// package.json (root)
{
"name": "my-monorepo",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"clean": "turbo run clean && rm -rf node_modules"
},
"devDependencies": {
"turbo": "^1.10.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0"
},
"packageManager": "[email protected]"
}


### Package Structure

// packages/ui/package.json
{
"name": "@repo/ui",
"version": "0.0.0",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./button": {
"import": "./dist/button.js",
"types": "./dist/button.d.ts"
}
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts",
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
"lint": "eslint src/",
"type-check": "tsc --noEmit"
},
"devDependencies": {
"@repo/tsconfig": "workspace:*",
"tsup": "^7.0.0",
"typescript": "^5.0.0"
},
"dependencies": {
"react": "^18.2.0"
}
}


## pnpm Workspaces

### Setup

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


// .npmrc
# Hoist shared dependencies
shamefully-hoist=true

# Strict peer dependencies
auto-install-peers=true
strict-peer-dependencies=true

# Performance
store-dir=~/.pnpm-store


### Dependency Management

# Install dependency in specific package
pnpm add react --filter @repo/ui
pnpm add -D typescript --filter @repo/ui

# Install workspace dependency
pnpm add @repo/ui --filter web

# Install in all packages
pnpm add -D eslint -w

# Update all dependencies
pnpm update -r

# Remove dependency
pnpm remove react --filter @repo/ui


### Scripts

# Run script in specific package
pnpm --filter web dev
pnpm --filter @repo/ui build

# Run in all packages
pnpm -r build
pnpm -r test

# Run in parallel
pnpm -r --parallel dev

# Filter by pattern
pnpm --filter "@repo/*" build
pnpm --filter "...web" build # Build web and dependencies


## Nx Monorepo

### Setup

# Create Nx monorepo
npx create-nx-workspace@latest my-org

# Generate applications
nx generate @nx/react:app my-app
nx generate @nx/next:app my-next-app

# Generate libraries
nx generate @nx/react:lib ui-components
nx generate @nx/js:lib utils


### Configuration

// nx.json
{
"extends": "nx/presets/npm.json",
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"inputs": ["production", "^production"],
"cache": true
},
"test": {
"inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
"cache": true
},
"lint": {
"inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
"cache": true
}
},
"namedInputs": {
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": [
"default",
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
"!{projectRoot}/tsconfig.spec.json"
],
"sharedGlobals": []
}
}


### Running Tasks

# Run task for specific project
nx build my-app
nx test ui-components
nx lint utils

# Run for affected projects
nx affected:build
nx affected:test --base=main

# Visualize dependencies
nx graph

# Run in parallel
nx run-many --target=build --all --parallel=3


## Shared Configurations

### TypeScript Configuration

// packages/tsconfig/base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"incremental": true,
"declaration": true
},
"exclude": ["node_modules"]
}

// packages/tsconfig/react.json
{
"extends": "./base.json",
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["ES2022", "DOM", "DOM.Iterable"]
}
}

// apps/web/tsconfig.json
{
"extends": "@repo/tsconfig/react.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}


### ESLint Configuration

// packages/config/eslint-preset.js
module.exports = {
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"prettier",
],
plugins: ["@typescript-eslint", "react", "react-hooks"],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 2022,
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: "detect",
},
},
rules: {
"@typescript-eslint/no-unused-vars": "error",
"react/react-in-jsx-scope": "off",
},
};

// apps/web/.eslintrc.js
module.exports = {
extends: ["@repo/config/eslint-preset"],
rules: {
// App-specific rules
},
};


## Code Sharing Patterns

### Pattern 1: Shared UI Components

// packages/ui/src/button.tsx
import * as React from 'react';

export interface ButtonProps {
variant?: 'primary' | 'secondary';
children: React.ReactNode;
onClick?: () => void;
}

export function Button({ variant = 'primary', children, onClick }: ButtonProps) {
return (
<button
className={btn btn-${variant}}
onClick={onClick}
>
{children}
</button>
);
}

// packages/ui/src/index.ts
export { Button, type ButtonProps } from './button';
export { Input, type InputProps } from './input';

// apps/web/src/app.tsx
import { Button } from '@repo/ui';

export function App() {
return <Button variant="primary">Click me</Button>;
}


### Pattern 2: Shared Utilities

// packages/utils/src/string.ts
export function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}

export function truncate(str: string, length: number): string {
return str.length > length ? str.slice(0, length) + "..." : str;
}

// packages/utils/src/index.ts
export * from "./string";
export * from "./array";
export * from "./date";

// Usage in apps
import { capitalize, truncate } from "@repo/utils";


### Pattern 3: Shared Types

// packages/types/src/user.ts
export interface User {
id: string;
email: string;
name: string;
role: "admin" | "user";
}

export interface CreateUserInput {
email: string;
name: string;
password: string;
}

// Used in both frontend and backend
import type { User, CreateUserInput } from "@repo/types";


## Build Optimization

### Turborepo Caching

// turbo.json
{
"pipeline": {
"build": {
// Build depends on dependencies being built first
"dependsOn": ["^build"],

// Cache these outputs
"outputs": ["dist/**", ".next/**"],

// Cache based on these inputs (default: all files)
"inputs": ["src/**/*.tsx", "src/**/*.ts", "package.json"]
},
"test": {
// Run tests in parallel, don't depend on build
"cache": true,
"outputs": ["coverage/**"]
}
}
}


### Remote Caching

# Turborepo Remote Cache (Vercel)
npx turbo login
npx turbo link

# Custom remote cache
# turbo.json
{
"remoteCache": {
"signature": true,
"enabled": true
}
}


## CI/CD for Monorepos

### GitHub Actions

# .github/workflows/ci.yml
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # For Nx affected commands

- uses: pnpm/action-setup@v2
with:
version: 8

- uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build
run: pnpm turbo run build

- name: Test
run: pnpm turbo run test

- name: Lint
run: pnpm turbo run lint

- name: Type check
run: pnpm turbo run type-check


### Deploy Affected Only

# Deploy only changed apps
- name: Deploy affected apps
run: |
if pnpm nx affected:apps --base=origin/main --head=HEAD | grep -q "web"; then
echo "Deploying web app"
pnpm --filter web deploy
fi


## Best Practices

1. **Consistent Versioning**: Lock dependency versions across workspace
2. **Shared Configs**: Centralize ESLint, TypeScript, Prettier configs
3. **Dependency Graph**: Keep it acyclic, avoid circular dependencies
4. **Cache Effectively**: Configure inputs/outputs correctly
5. **Type Safety**: Share types between frontend/backend
6. **Testing Strategy**: Unit tests in packages, E2E in apps
7. **Documentation**: README in each package
8. **Release Strategy**: Use changesets for versioning

## Common Pitfalls

- **Circular Dependencies**: A depends on B, B depends on A
- **Phantom Dependencies**: Using deps not in package.json
- **Incorrect Cache Inputs**: Missing files in Turborepo inputs
- **Over-Sharing**: Sharing code that should be separate
- **Under-Sharing**: Duplicating code across packages
- **Large Monorepos**: Without proper tooling, builds slow down

## Publishing Packages

# Using Changesets
pnpm add -Dw @changesets/cli
pnpm changeset init

# Create changeset
pnpm changeset

# Version packages
pnpm changeset version

# Publish
pnpm changeset publish


# .github/workflows/release.yml
- name: Create Release Pull Request or Publish
uses: changesets/action@v1
with:
publish: pnpm release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}


## Resources

- **references/turborepo-guide.md**: Comprehensive Turborepo documentation
- **references/nx-guide.md**: Nx monorepo patterns
- **references/pnpm-workspaces.md**: pnpm workspace features
- **assets/monorepo-checklist.md**: Setup checklist
- **assets/migration-guide.md**: Multi-repo to monorepo migration
- **scripts/dependency-graph.ts**: Visualize package dependencies

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/monorepo-management/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/wshobson/agents/monorepo-management/SKILL.md
  • Cursor: ~/.cursor/skills/wshobson/agents/monorepo-management/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/wshobson/agents/monorepo-management/SKILL.md

πŸš€ Install with CLI:
npx skills add wshobson/agents

Read the Master Guide: Mastering Agent Skills β†’

Recommended Rules

View more rules β†’

Recommended Workflows

View more workflows β†’

Recommended MCP Servers

View more MCP servers β†’

Take It Further

Maximize your productivity with these powerful resources

πŸ“‹

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
πŸ“–

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid architecture & design patterns issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Architecture & Design Patterns and is published by W. Shobson, maintained in wshobson/agents.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.