← Back to list

Building Your First Turborepo

In the previous article, we explored what Monorepos and Turborepos are, why companies use them, and how Turborepo acts as a…

Muskanqed · 2026-06-04 18:08 · 0 claps · 6.9 min read
#monorepo #nx #lerna #turborepo #workspace
Open on Medium ↗
Wiki topics: 🔭 · Astronomy & Space

Building Your First Turborepo

In the previous article, we explored what Monorepos and Turborepos are, why companies use them, and how Turborepo acts as a high-performance build system and task orchestrator for modern JavaScript and TypeScript projects.

Reference Link for Monorepos and Turborepos

In this article, we’ll move away from theory and build a Turborepo from scratch.

By the end of this guide, you’ll understand:

  • How to create a Turborepo project.
  • The command used to initialize it.
  • The default folder structure.
  • The purpose of the apps and packages directories.
  • How multiple applications can coexist inside a single repository.
  • How shared packages are organized and reused

Prerequisites

Before creating a Turborepo, make sure you have:

  • Node.js installed.
  • A package manager such as npm, pnpm, or Yarn.
  • Basic knowledge of Git and JavaScript/TypeScript projects.

Although Turborepo supports multiple package managers, this guide will use the default setup generated by the official CLI.

Creating a New Turborepo

The easiest way to get started is by using the official scaffolding command:

npx create-turbo@latest

After running the command, the CLI will ask you a few simple questions, such as the project name and the package manager you want to use.

Once the setup is complete, a fully configured Turborepo project will be generated automatically.

At first glance, the generated structure may seem larger than a typical frontend or backend project, but each folder has a specific purpose.

In the next section, we’ll break down the generated folder structure and understand why Turborepo organizes projects this way.

Understanding the Default Project Structure

When you create a new Turborepo using the official starter template, you’ll typically see two major directories:

apps/

The apps directory contains the actual applications that can be built and deployed independently.

By default, the starter template generates:

  • **apps/web** — A Next.js application that usually serves as the primary user-facing web application.
  • **apps/docs** — Another Next.js application, commonly used to host project documentation or component documentation.

Although both are independent Next.js projects, they live inside the same repository, which is one of the core ideas behind a monorepo.

packages/

The packages directory contains code and configurations that can be shared across multiple applications.

The default template usually includes:

  • **packages/ui** — A shared UI component library. Components created here can be imported and reused by both the web and docs applications.
  • **packages/typescript-config** — A centralized TypeScript configuration package. Instead of maintaining separate tsconfig.json files for every application, common settings are shared from here.
  • **packages/eslint-config** — A centralized ESLint configuration package that helps maintain consistent coding standards across the entire monorepo.

The main purpose of the packages directory is to avoid duplication and encourage code sharing between applications.

Running the Turborepo

Now that we understand the folder structure, let’s actually run the project.

From the root directory, execute:

npm run dev

Note: Depending on the version of the Turborepo starter template, you may need a relatively recent version of Node.js. If you encounter compatibility errors, simply upgrade your Node.js installation and try again.

After the command executes successfully, you should notice two development servers starting automatically.

Typically, they will be available at:

The exact ports may vary depending on your local environment, but by default one serves the web application and the other serves the docs application.

What Is Actually Happening?

At first glance, it may look like both Next.js applications are being started magically.

However, this is where Turborepo demonstrates its real purpose.

When you execute:

npm run dev

from the root of the repository, the root package.json delegates the task to Turborepo.

Turborepo then scans the entire monorepo and looks for projects that define a dev script.

For example:

apps/
├── web/
│   └── package.json
└── docs/
    └── package.json

Both of these applications contain their own development command:

{
  "scripts": {
    "dev": "next dev"
  }
}

Turborepo does not start Next.js itself.

Instead, it orchestrates these tasks by discovering the available dev scripts and executing them together.

In simple words:

  • Next.js knows how to run the application.
  • Turborepo knows which applications should run and coordinates them.

This is exactly why Turborepo is called a task orchestrator rather than a build tool.

A Practical Observation

By running a single command from the root directory, we are simultaneously running multiple independent applications that live inside one repository.

This is one of the fundamental characteristics of a monorepo:

A single repository containing multiple independent projects that can be developed and managed together.

In our case:

  • apps/web → Main web application
  • apps/docs → Documentation application

Both are separate Next.js projects, but they coexist inside the same repository and are coordinated by Turborepo.

Adding Your Own Applications

The applications generated by the Turborepo starter template are only examples.

The web and docs applications are created using Next.js because they provide a good starting point, but a Turborepo is not limited to Next.js.

You can add any kind of application to your monorepo:

  • Express.js backend
  • React application created with Vite
  • NestJS server
  • Node.js microservice
  • CLI applications
  • Shared internal tools

As long as the application exposes standard scripts such as dev and build, Turborepo can orchestrate them.

Adding a Backend Server

Suppose we want to add an Express.js backend to our existing monorepo.

First, navigate to the apps directory:

cd apps

Now create a new folder for the backend:

mkdir server
cd server

Initialize a new Node.js project:

npm init -y

Install Express:

npm install express

For a TypeScript setup, you may also install:

npm install -D typescript tsx @types/node @types/express

Your folder structure may now look like:

apps/
├── web/
├── docs/
└── server/

At this point, the server is simply another application inside the monorepo.

Defining Development and Build Scripts

Inside the server’s package.json, define the scripts that Turborepo should orchestrate.

For example:

{
  "scripts": {
    "dev": "tsx src/index.ts",
    "build": "tsc"
  }
}

Notice that Turborepo itself is not building the server.

The server knows how to run itself.

Turborepo simply discovers these scripts and coordinates them.

Running the New Application

Once the server has been added, you do not need to manually start it every time.

Running:

npm run dev

from the root of the repository allows Turborepo to discover the new dev script and execute it alongside the other applications.

Conceptually, Turborepo now sees something like:

apps/
├── web      → next dev
├── docs     → next dev
└── server   → tsx src/index.ts

A single command can now start multiple independent applications that belong to the same repository.

The Real Power of Turborepo: Intelligent Caching

At this point, you might wonder:

“If Turborepo simply runs the build scripts defined by each application, why do so many companies use it?”

The answer is intelligent caching.

The First Build

Suppose we execute:

npm run build

for the very first time.

Internally, the root package.json delegates this command to:

turbo build

Since no previous build information exists, Turborepo has no cached results available.

It discovers every project that exposes a build script and executes them.

For example:

apps/
├── web
└── docs
packages/
├── ui
├── eslint-config
└── typescript-config

During this first execution, all required build tasks run normally.

Once they complete successfully, Turborepo stores information about these builds inside its local cache.

What Happens If Nothing Changes?

Now suppose you immediately run:

npm run build

again without modifying any files.

This time, Turborepo compares the current state of the repository with the previous build.

Since nothing has changed, it recognizes that the previous build outputs are still valid.

Instead of rebuilding everything again, Turborepo simply restores the results from its cache.

In other words:

First Build:
❌ Cache Miss
✅ Build Everything
Second Build:
✅ Cache Hit
❌ Rebuild Nothing

This is one of the main reasons why Turborepo can significantly reduce build times.

What Happens If We Change One Application?

Now imagine we make a small change inside:

apps/web

The docs application does not depend on web.

Therefore, rebuilding docs would be unnecessary.

Turborepo analyzes the dependency graph and determines that only the web application has been affected.

The next build behaves like this:

apps/web   → Rebuild
apps/docs  → Use Cache

Only the modified project is rebuilt, while the unaffected project reuses its previously cached result.

What Happens If We Change a Shared Package?

Now consider a different situation.

Suppose we modify a shared component inside:

packages/ui

Both the web and docs applications import components from this package.

Because these applications depend on packages/ui, Turborepo knows that their previous build outputs are no longer valid.

The next build will look something like:

packages/ui  → Rebuild
apps/web     → Rebuild
apps/docs    → Rebuild

This happens because a shared dependency has changed.

Rather than blindly rebuilding every project in the repository, Turborepo rebuilds only the projects that are actually affected.

Why This Matters

Imagine a large monorepo containing:

  • 10 frontend applications
  • 5 backend services
  • 8 shared packages

Without intelligent caching, every small change could potentially trigger dozens of unnecessary builds.

Turborepo avoids this by understanding the relationships between projects and only rebuilding what is required.

This dependency-aware caching mechanism is one of the primary reasons why Turborepo is considered a high-performance build system and task orchestrator for modern monorepos.

It doesn’t simply execute tasks — it remembers previous work and intelligently decides when that work can be safely reused.

Final Thoughts

In this article, we moved beyond the theory of Monorepos and Turborepos and explored how a Turborepo actually works in practice.

We started by creating a new Turborepo project and understanding the default folder structure. We saw how the repository is divided into two major parts:

  • apps/ — which contains independent applications such as web and docs.
  • packages/ — which contains shared code and configurations that can be reused across multiple applications.

We then explored how Turborepo uses the root package.json as an entry point. Instead of building or running applications itself, Turborepo discovers the scripts defined inside each individual project's package.json and orchestrates their execution.

We also learned that adding a new application is straightforward. Whether it is an Express.js backend, a React application, or another service, as long as it exposes standard scripts like dev and build, Turborepo can automatically include it in the development and build workflow.

Finally, we looked at one of Turborepo’s most powerful features: intelligent caching. Rather than rebuilding every application after every small change, Turborepo understands the dependency graph of the monorepo and rebuilds only the projects that are actually affected, while reusing cached results for everything else.

My Biggest Takeaway

The biggest realization for me while learning Turborepo was that it is not another frontend framework or build tool. It is a task orchestrator that sits on top of existing tools and coordinates them intelligently.

A monorepo gives us a way to organize multiple related projects inside a single repository, while Turborepo provides the infrastructure to manage those projects efficiently through orchestration, dependency awareness, and caching.

As projects grow larger and more complex, these concepts become increasingly valuable, which is why many modern startups and product companies adopt this architecture for their applications.


메타데이터
post_id
d77cd631b56a
slug
building-your-first-turborepo-d77cd631b56a
url
https://medium.com/@muskanqed/building-your-first-turborepo-d77cd631b56a
canonical_url
https://medium.com/@muskanqed/building-your-first-turborepo-d77cd631b56a
author_url
https://medium.com/@muskanqed
status
ok
fetched_at
2026-06-10 08:17:25