Supercharge Your Development with Turborepo: A Practical Guide


Never miss an update
Subscribe to receive news and special offers.
By subscribing you agree to our Privacy Policy.
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.
Turborepo is a high-performance build system for JavaScript and TypeScript codebases. It's designed to make monorepo management efficient through:
Whether you're a solo developer or part of a large team, Turborepo can dramatically improve your development experience.
Let's start with a fresh project. You can either create a new Turborepo from scratch or add it to an existing project.
# Using npx
npx create-turbo@latest my-turborepo
# Using yarn
yarn create turbo my-turborepo
# Using pnpm
pnpm create turbo my-turborepoThis creates a starter monorepo with a sample structure that includes:
apps/ - Your applicationspackages/ - Shared packages/librariesturbo.json - Turborepo configurationIf you already have a project, you can add Turborepo with:
# Using npm
npm install turbo --save-dev
# Using yarn
yarn add turbo --dev
# Using pnpm
pnpm add turbo --save-devThen create a turbo.json file in your project root:
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
}
}
}The heart of Turborepo is the turbo.json file, which defines your task pipeline. Let's break down the key components:
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
}
}This tells Turborepo:
build script in each package^build means)dist/ and .next/ directories as outputsdependsOnThe 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 firstoutputsThe outputs array tells Turborepo what files to cache:
["dist/**"] - Cache everything in the dist/ directory[] - Don't cache anything (good for linting or testing)Let's set up a practical monorepo with a Next.js frontend and a shared UI library:
my-turborepo/
βββ apps/
β βββ web/ # Next.js app
βββ packages/
β βββ ui/ # Shared UI components
βββ package.json
βββ turbo.jsonpackage.json{
"name": "my-turborepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "latest"
}
}turbo.json{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"outputs": []
},
"test": {
"dependsOn": ["build"],
"outputs": [],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
}
}
}In packages/ui/package.json:
{
"name": "ui",
"version": "0.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsup src/index.tsx --format esm,cjs --dts --external react",
"dev": "tsup src/index.tsx --format esm,cjs --watch --dts --external react",
"lint": "eslint src/**/*.ts*",
"test": "jest"
},
"devDependencies": {
"tsup": "^6.5.0",
"typescript": "^4.9.4"
},
"peerDependencies": {
"react": "^18.2.0"
}
}In apps/web/package.json:
{
"name": "web",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "^13.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ui": "*"
},
"devDependencies": {
"@types/node": "^18.11.18",
"@types/react": "^18.0.26",
"typescript": "^4.9.4"
}
}Now that we have our monorepo set up, let's see how to run tasks:
turbo run buildThis builds all packages and apps in the correct order, with caching enabled.
turbo run devThis starts development servers for all packages and apps in parallel.
# Build only the web app
turbo run build --filter=web
# Build the web app and its dependencies
turbo run build --filter=web...
# Build everything except the web app
turbo run build --filter=!webFor team environments, Turborepo offers remote caching to share build artifacts across developers and CI/CD:
# Login to Vercel (if using Vercel's remote cache)
npx turbo login
# Link your project
npx turbo link
# Run with remote caching enabled
turbo run build --team="your-team"Be specific about task dependencies to avoid unnecessary work:
"build": {
"dependsOn": ["^build", "test"],
"outputs": ["dist/**"]
}Turborepo can prune your workspace to only the packages affected by a change:
turbo prune --scope=webThis creates a smaller subset of your monorepo containing only what's needed to build the specified package.
# In your CI setup
TURBO_TEAM="your-team" TURBO_TOKEN="xxx" turbo run buildNot everything needs to be a separate package. Group related functionality and avoid creating too many tiny packages.
Turborepo shines in these scenarios:
It might not be the best fit for:
Check that:
outputs are correctly specifiedVerify your dependsOn configurations and ensure your dependency graph is correct.
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:
Ready to try a specific command?
# For most projects, start with:
turbo run dev --filter=apps/webFor single-package projects:
turbo run dev --filter=apps/webπ Single-package projects gain little from turbo dev due to lack of parallel tasks and caching.
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!