How to Set Up a Monorepo with Turborepo in 2026: A Practical Guide

Why Monorepos Are Worth the Setup Cost

A monorepo puts multiple related projects in a single repository. For teams building a web app, a mobile app, shared component libraries, and backend services, the alternative is managing a half-dozen separate repositories with their own dependencies, version alignment, and deployment pipelines. The coordination cost compounds quickly.

Turborepo makes monorepo management practical by solving the two main pain points: incremental builds and task orchestration. It understands which packages changed, runs only the tasks affected by those changes, and caches results so that repeated builds across CI runs are fast. The difference between a 15-minute CI run that rebuilds everything and a 2-minute run that rebuilds only what changed matters when you are shipping multiple times per day.

Scaffolding the Repository

Start with the official scaffold: npx create-turbo@latest. This creates a workspace with a standard structure: apps for deployable applications, packages for shared libraries, and a root package.json configured for the workspace.

If you are adding Turborepo to an existing repository, the manual setup is straightforward. Add turbo to the root devDependencies, create a turbo.json at the root to define your pipeline, and make sure your package manager workspace configuration is correct. Turborepo supports npm, yarn, pnpm, and bun workspaces.

Use pnpm if you are starting fresh. Its workspace protocol and strict dependency hoisting prevent the phantom dependency problems that npm workspaces can create, and Turborepo works particularly well with pnpm.

Defining the Pipeline in turbo.json

The turbo.json pipeline defines how your tasks relate to each other. A typical configuration defines build, test, lint, and dev tasks, specifying which outputs should be cached and which tasks depend on upstream packages completing first.

For a build task, set outputs to the directories where build artifacts are produced and set dependsOn to ^build to indicate that packages must be built before their dependents. Turborepo uses this graph to determine the correct execution order and parallelism.

Cache configuration is where Turborepo earns its value. Specify the outputs correctly and Turborepo will restore them from cache rather than rebuilding, both locally and on CI. For most build pipelines, this means the first run is slow and every subsequent run on unchanged packages is near-instant.

Shared Packages Structure

The packages directory holds shared code. A typical setup has packages for the design system, utility functions, TypeScript configuration, ESLint configuration, and type definitions. Each package has its own package.json with a name that starts with your workspace namespace.

Configure TypeScript composite projects for shared packages. This enables incremental TypeScript compilation and allows the TypeScript language server to understand cross-package imports in the editor. The tsconfig.base.json at the root defines shared compiler options that each package extends.

CI Configuration

Turborepo Remote Cache enables the same caching behavior in CI that you get locally. Vercel hosts a free remote cache if you deploy there, or you can self-host using an S3-compatible store with the open-source cache server.

For GitHub Actions, the workflow is straightforward: restore the Turborepo cache between runs, run turbo run build test lint, and save the cache. On subsequent runs, only changed packages are rebuilt. With remote cache active, CI runs that touch only one package in a large monorepo complete in a fraction of the time of a full rebuild.

Getting the Benefits

The returns on the Turborepo setup become visible quickly: faster CI, clearer code sharing boundaries, and a single place to make infrastructure changes that affect all projects. The upfront investment in configuration pays back within the first few weeks for teams working on multiple related products.