Vercel Logo

Monorepo Setup with Turborepo

Microfrontends don't require a monorepo, but shared code and coordinated builds are easier with one.

Outcome

Set up a Turborepo monorepo with workspace configuration, task orchestration, and remote caching.

Why Monorepo + Microfrontends

BenefitHow It Helps
Shared packagesOne Header component, used everywhere
Atomic commitsChange shared code and all consumers together
Coordinated buildsTurborepo builds dependencies in the right order
Remote cachingSkip builds that haven't changed

Vercel uses this pattern: all 12 microfrontend projects live in one monorepo with shared packages for headers, footers, and design systems.

Fast Track

  1. Create a new directory and initialize Turborepo
  2. Configure pnpm workspaces
  3. Verify the setup runs

Hands-on Exercise 1.3

Initialize the Acme Platform monorepo from scratch.

Requirements:

  1. Create a new directory called acme-platform
  2. Initialize with pnpm and Turborepo
  3. Configure workspaces for apps/* and packages/*
  4. Set up the turbo.json task configuration

Step 1: Create the directory structure

mkdir acme-platform
cd acme-platform

Step 2: Initialize package.json

Create package.json:

package.json
{
  "name": "acme-platform",
  "private": true,
  "scripts": {
    "dev": "turbo dev",
    "build": "turbo build",
    "lint": "turbo lint"
  },
  "devDependencies": {
    "turbo": "^2.3.0"
  },
  "packageManager": "pnpm@9.15.0"
}

Step 3: Configure pnpm workspaces

Create pnpm-workspace.yaml:

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

Step 4: Configure Turborepo

Create turbo.json:

turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^lint"]
    }
  }
}

Step 5: Create directory structure

mkdir -p apps packages

Step 6: Install dependencies

pnpm install

Try It

After setup, your structure should look like:

acme-platform/
├── apps/                    # Empty for now
├── packages/                # Empty for now
├── node_modules/
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── turbo.json

Verify Turborepo is working:

pnpm turbo --version

Expected output:

2.3.0

Understanding turbo.json

The configuration tells Turborepo how tasks relate:

{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],  // Build dependencies first
      "outputs": [".next/**", "!.next/cache/**"]  // Cache these files
    },
    "dev": {
      "cache": false,     // Don't cache dev mode
      "persistent": true  // Keep running
    }
  }
}
  • dependsOn: ["^build"] - The ^ means "build my dependencies before building me"
  • outputs - Files Turborepo caches to skip future builds
  • persistent: true - The task keeps running (like a dev server)

Commit

git init
git add -A
git commit -m "chore: initialize turborepo monorepo"

Done-When

  • acme-platform directory created
  • package.json with turbo scripts configured
  • pnpm-workspace.yaml pointing to apps/* and packages/*
  • turbo.json with build, dev, and lint tasks
  • pnpm install completes without errors

Optional: Enable Remote Caching

Remote caching shares build artifacts across machines. Enable it with Vercel:

npx turbo login
npx turbo link

After linking, builds on CI will reuse cached artifacts from local development (and vice versa). This becomes valuable once you have multiple applications building.

What's Next

Skeleton's ready. Now you'll create the three Next.js apps and a shared UI package.