Supercharge Your Development with Turborepo: A Practical Guide

Andry Dina
Turborepo Development Workflow

Supercharge Your Development with Turborepo: A Practical Guide

If you've ever worked on a project with multiple packages or apps, you know the pain: slow builds, inconsistent tooling, and the constant struggle to keep everything in sync. Turborepo solves these problems with an intelligent build system that makes monorepo management feel like a superpower.

In this guide, I'll walk you through setting up Turborepo and optimizing your workflow for maximum productivity.

What is Turborepo and Why Should You Care?

Turborepo is a high-performance build system for JavaScript and TypeScript codebases. It's designed to make monorepo management efficient through:

  • ⚔ Incremental builds - Only rebuild what changed
  • šŸ”„ Parallel execution - Run tasks across packages simultaneously
  • šŸ’¾ Intelligent caching - Never rebuild the same code twice
  • šŸ” Dependency graph - Understand relationships between packages

Whether you're a solo developer or part of a large team, Turborepo can dramatically improve your development experience.

Getting Started: Setting Up Your First Turborepo

Let's start with a fresh project. You can either create a new Turborepo from scratch or add it to an existing project.

Option 1: Create a New Turborepo

bash
1# Using npx
2npx create-turbo@latest my-turborepo
3
4# Using yarn
5yarn create turbo my-turborepo
6
7# Using pnpm
8pnpm create turbo my-turborepo

This creates a starter monorepo with a sample structure that includes:

  • apps/ - Your applications
  • packages/ - Shared packages/libraries
  • turbo.json - Turborepo configuration

Option 2: Add Turborepo to an Existing Project

If you already have a project, you can add Turborepo with:

bash
1# Using npm
2npm install turbo --save-dev
3
4# Using yarn
5yarn add turbo --dev
6
7# Using pnpm
8pnpm add turbo --save-dev

Then create a turbo.json file in your project root:

json
1{
2  "$schema": "https://turbo.build/schema.json",
3  "pipeline": {
4    "build": {
5      "dependsOn": ["^build"],
6      "outputs": ["dist/**", ".next/**"]
7    },
8    "lint": {
9      "outputs": []
10    },
11    "dev": {
12      "cache": false,
13      "persistent": true
14    }
15  }
16}

Understanding the Turborepo Configuration

The heart of Turborepo is the turbo.json file, which defines your task pipeline. Let's break down the key components:

Pipeline Tasks

json
1"pipeline": {
2  "build": {
3    "dependsOn": ["^build"],
4    "outputs": ["dist/**", ".next/**"]
5  }
6}

This tells Turborepo:

  • Run the build script in each package
  • Before building a package, build all its dependencies first (that's what ^build means)
  • Cache the dist/ and .next/ directories as outputs

Task Dependencies with dependsOn

The dependsOn array defines prerequisites for a task:

  • ["^build"] - Run build on all dependencies first
  • ["build"] - Run the build task in the same package first
  • ["$TASK_NAME"] - Run a specific task in the same package first

Caching with outputs

The outputs array tells Turborepo what files to cache:

  • ["dist/**"] - Cache everything in the dist/ directory
  • [] - Don't cache anything (good for linting or testing)

Practical Example: Setting Up a Full-Stack Monorepo

Let's set up a practical monorepo with a Next.js frontend and a shared UI library:

typescript
1my-turborepo/
2ā”œā”€ā”€ apps/
3│   └── web/             # Next.js app
4ā”œā”€ā”€ packages/
5│   └── ui/              # Shared UI components
6ā”œā”€ā”€ package.json
7└── turbo.json

1. Configure the Root package.json

json
1{
2  "name": "my-turborepo",
3  "private": true,
4  "workspaces": [
5    "apps/*",
6    "packages/*"
7  ],
8  "scripts": {
9    "build": "turbo run build",
10    "dev": "turbo run dev",
11    "lint": "turbo run lint",
12    "test": "turbo run test"
13  },
14  "devDependencies": {
15    "turbo": "latest"
16  }
17}

2. Configure turbo.json

json
1{
2  "$schema": "https://turbo.build/schema.json",
3  "pipeline": {
4    "build": {
5      "dependsOn": ["^build"],
6      "outputs": ["dist/**", ".next/**"]
7    },
8    "dev": {
9      "cache": false,
10      "persistent": true
11    },
12    "lint": {
13      "outputs": []
14    },
15    "test": {
16      "dependsOn": ["build"],
17      "outputs": [],
18      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
19    }
20  }
21}

3. Create the UI Package

In packages/ui/package.json:

json
1{
2  "name": "ui",
3  "version": "0.0.0",
4  "main": "./dist/index.js",
5  "types": "./dist/index.d.ts",
6  "scripts": {
7    "build": "tsup src/index.tsx --format esm,cjs --dts --external react",
8    "dev": "tsup src/index.tsx --format esm,cjs --watch --dts --external react",
9    "lint": "eslint src/**/*.ts*",
10    "test": "jest"
11  },
12  "devDependencies": {
13    "tsup": "^6.5.0",
14    "typescript": "^4.9.4"
15  },
16  "peerDependencies": {
17    "react": "^18.2.0"
18  }
19}

4. Set Up the Next.js App

In apps/web/package.json:

json
1{
2  "name": "web",
3  "version": "0.0.0",
4  "private": true,
5  "scripts": {
6    "dev": "next dev",
7    "build": "next build",
8    "start": "next start",
9    "lint": "next lint"
10  },
11  "dependencies": {
12    "next": "^13.1.1",
13    "react": "^18.2.0",
14    "react-dom": "^18.2.0",
15    "ui": "*"
16  },
17  "devDependencies": {
18    "@types/node": "^18.11.18",
19    "@types/react": "^18.0.26",
20    "typescript": "^4.9.4"
21  }
22}

Turborepo in Action: Running Tasks

Now that we have our monorepo set up, let's see how to run tasks:

Building Everything

bash
1turbo run build

This builds all packages and apps in the correct order, with caching enabled.

Running Development Servers

bash
1turbo run dev

This starts development servers for all packages and apps in parallel.

Filtering Tasks to Specific Packages

bash
1# Build only the web app
2turbo run build --filter=web
3
4# Build the web app and its dependencies
5turbo run build --filter=web...
6
7# Build everything except the web app
8turbo run build --filter=!web

Advanced Features: Remote Caching

For team environments, Turborepo offers remote caching to share build artifacts across developers and CI/CD:

bash
1# Login to Vercel (if using Vercel's remote cache)
2npx turbo login
3
4# Link your project
5npx turbo link
6
7# Run with remote caching enabled
8turbo run build --team="your-team"

Performance Tips and Best Practices

1. Optimize Your Task Dependencies

Be specific about task dependencies to avoid unnecessary work:

json
1"build": {
2  "dependsOn": ["^build", "test"],
3  "outputs": ["dist/**"]
4}

2. Use Pruning in CI

Turborepo can prune your workspace to only the packages affected by a change:

bash
1turbo prune --scope=web

This creates a smaller subset of your monorepo containing only what's needed to build the specified package.

3. Leverage Environment Variables for Configuration

bash
1# In your CI setup
2TURBO_TEAM="your-team" TURBO_TOKEN="xxx" turbo run build

4. Use Workspaces Wisely

Not everything needs to be a separate package. Group related functionality and avoid creating too many tiny packages.

When to Use Turborepo vs. Other Tools

Turborepo shines in these scenarios:

  • šŸ—ļø JavaScript/TypeScript monorepos with multiple apps/packages
  • šŸš€ Projects where build performance is critical
  • 🧩 Teams working on related packages simultaneously

It might not be the best fit for:

  • šŸ” Single-package projects with no shared code
  • 🌐 Polyglot monorepos with many non-JS languages

Troubleshooting Common Issues

Caching Not Working

Check that:

  • Your outputs are correctly specified
  • You're not modifying cached files during the build
  • Environment variables are consistent

Tasks Running in the Wrong Order

Verify your dependsOn configurations and ensure your dependency graph is correct.

Conclusion: Scaling Your Development with Turborepo

Turborepo transforms how you manage complex JavaScript projects by making builds faster, more reliable, and easier to reason about. Start with a simple configuration and gradually optimize as your project grows.

Key takeaways:

  • šŸš€ Turborepo dramatically speeds up builds through intelligent caching
  • šŸ“¦ It works with any package manager (npm, yarn, pnpm)
  • ļæ½ The pipeline configuration gives you fine-grained control
  • 🌐 Remote caching enables team-wide performance benefits

Ready to try a specific command?

bash
1# For most projects, start with:
2turbo run dev --filter=apps/web

For single-package projects:

bash
1turbo run dev --filter=apps/web

šŸ“ Single-package projects gain little from turbo dev due to lack of parallel tasks and caching.

Additional Resources

Turborepo empowers you to scale development smoothly, whether you're working solo or across dozens of teams. Start small. Scale fast.


Have you tried Turborepo in your projects? What performance improvements did you see? Let me know in the comments below!

Join our newsletter for the
latest update

By subscribing you agree to receive the Paddle newsletter. Unsubscribe at any time.