Setting Up a Monorepo with Turborepo in 2026


I resisted monorepos for a long time. Multiple repos felt cleaner - each project has its own dependencies, its own CI, its own deployment. Simple.

Then I started working on a project with a Next.js frontend, a separate API service, a shared component library, and a shared types package. Four repos. Four sets of dependencies. Four CI pipelines. And the constant headache of keeping shared code synchronised across all of them.

I switched to a Turborepo monorepo six months ago and I’m not going back. The initial setup takes a bit of thought, but once it’s running, the development experience is dramatically better. Here’s how to set one up properly in 2026.

Why Turborepo

There are several monorepo tools available - Nx, Lerna (still alive, somehow), pnpm workspaces, and Turborepo. I chose Turborepo because:

  1. It’s fast. Turborepo’s caching system means it only rebuilds what’s changed. On a project with multiple packages, this cuts build times significantly.
  2. It’s simple. Compared to Nx, which is powerful but has a steeper learning curve, Turborepo is straightforward to configure.
  3. It works with any package manager. pnpm, npm, yarn - all supported. I use pnpm because it’s the fastest and most disk-efficient.
  4. Remote caching. Turborepo can cache build outputs remotely via Vercel, meaning your CI pipelines benefit from caching across runs.

Initial Setup

Start by creating a new Turborepo project:

npx create-turbo@latest my-project

This gives you a scaffold with a basic monorepo structure. But I usually prefer setting things up manually to understand what’s happening. Here’s the manual approach:

mkdir my-project && cd my-project
pnpm init

Create a pnpm-workspace.yaml file:

packages:
  - "apps/*"
  - "packages/*"

Install Turborepo:

pnpm add -D turbo

Create your turbo.json:

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

The key thing here is dependsOn: ["^build"]. The caret (^) means “run this task in dependencies first.” So when you build your Next.js app, it will first build any packages it depends on.

Project Structure

Here’s the structure I use for most projects:

my-project/
  apps/
    web/          # Next.js frontend
    api/          # Express/Fastify API
  packages/
    ui/           # Shared React components
    types/        # Shared TypeScript types
    config/       # Shared config (ESLint, TypeScript, etc.)
    utils/        # Shared utility functions
  turbo.json
  pnpm-workspace.yaml
  package.json

Each directory under apps/ and packages/ is its own package with its own package.json. The monorepo manages the relationships between them.

Setting Up a Shared Package

Let’s set up the packages/types package as an example:

mkdir -p packages/types && cd packages/types
pnpm init

Edit packages/types/package.json:

{
  "name": "@my-project/types",
  "version": "0.0.1",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch"
  }
}

Add a tsconfig.json and your type definitions. The key is using internal package names (prefixed with @my-project/) that other packages in the monorepo can reference.

In your Next.js app, add the dependency:

{
  "dependencies": {
    "@my-project/types": "workspace:*"
  }
}

The workspace:* syntax tells pnpm to resolve this package from the local workspace rather than npm registry. Run pnpm install to link everything up.

The Stuff That Trips People Up

After setting up several Turborepo monorepos, here are the gotchas I’ve hit:

TypeScript path resolution. If you’re using TypeScript path aliases (@/components, etc.) within individual packages, make sure your tsconfig.json files are consistent. I use a shared base config in packages/config/tsconfig-base.json that all packages extend.

Dependency hoisting. pnpm hoists dependencies differently than npm or yarn. Sometimes a package works in isolation but fails in the monorepo because a dependency it relies on is hoisted to the root. Add a .npmrc file with shamefully-hoist=true if you hit persistent resolution issues (though try to fix them properly first).

Environment variables. Each app in the monorepo needs its own .env file. Turborepo doesn’t share environment variables across packages. Make sure your .gitignore covers .env files in all apps, not just the root.

CI caching. Turborepo’s remote caching with Vercel is free for personal use and works well. Add TURBO_TOKEN and TURBO_TEAM to your CI environment variables. The first build takes full time, but subsequent builds with unchanged packages are nearly instant.

Running Everything

Add these scripts to your root package.json:

{
  "scripts": {
    "dev": "turbo dev",
    "build": "turbo build",
    "lint": "turbo lint",
    "test": "turbo test"
  }
}

pnpm dev starts all apps and packages in dev mode simultaneously. Turborepo handles the dependency ordering - shared packages build before apps that depend on them.

pnpm build builds everything with intelligent caching. If you haven’t changed packages/types, it won’t rebuild it. This matters a lot as your project grows.

Is It Worth It?

For a single app with no shared code, a monorepo is overkill. But the moment you have two apps sharing types, components, or utility functions, the monorepo approach saves you real time and frustration.

The investment in setup (about 2-3 hours for a well-structured monorepo) pays off within a week of development. No more “did I remember to publish the types package” or “why is the component library out of sync.”

Turborepo specifically is the tool I’d recommend for most JavaScript/TypeScript projects. It’s opinionated enough to get you started quickly but flexible enough to handle complex setups. The Turborepo documentation has improved significantly over the past year and covers most scenarios well.

Give it a try on your next project. Your future self will thank you.