Managing Dependencies

Purpose and Scope

This guide explains how to manage dependencies inside a Turborepo workspace. In this context, a dependency is either external, meaning it comes from the npm ecosystem, or internal, meaning it is another package in the same repository. Turborepo’s role is to understand the resulting package relationships so it can run tasks efficiently; it is not the tool that installs, upgrades, or resolves packages. The practical outcome is simple: declare what each package uses, let the package manager maintain the install and lockfile, and let Turborepo use those declarations for task orchestration.

Sources: apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx, apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx

A healthy workspace makes dependency ownership visible at the package boundary. If an application imports a framework, testing library, or internal UI package, that application should list the dependency in its own package manifest. This convention applies to both external and internal dependencies. The source guide emphasizes that direct declaration improves clarity because developers can inspect a package and see what it needs, and it improves flexibility because large monorepos may legitimately run different versions of an external dependency in different packages.

Sources: apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx

Relevant Source Files

  • apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx - Primary guide for external dependencies, internal dependencies, package-manager examples, installation location, and the statement that Turborepo does not manage dependencies.
  • apps/docs/content/docs/crafting-your-repository/caching.mdx - Explains why dependency and input changes matter to task fingerprints, local cache restoration, and Remote Caching behavior.
  • apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx - Shows how package relationships become task ordering through task definitions and dependency-aware dependsOn configuration.
  • apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx - Connects dependency management to CI by describing remote cache credentials, consistent task execution, and filtering in pipelines.
  • apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx - Demonstrates how internal packages become discoverable through package manifests and then participate in the Package Graph.
  • apps/docs/content/docs/crafting-your-repository/developing-applications.mdx - Shows how development tasks, persistent processes, setup tasks, and filters operate against the same workspace graph.

Core Dependency Model

External dependencies and internal dependencies should be treated as first-class package requirements, not as incidental files at the repository root. The managing-dependencies guide illustrates this with an application manifest that depends on an external framework such as Next.js and an internal package such as a repository UI library. Package managers differ in the preferred specifier for workspace dependencies: pnpm and Bun commonly use a workspace protocol, while npm and Yarn examples use a broad local workspace-compatible range. The important rule is not the exact string alone; it is that the consuming package declares the package it imports.

Sources: apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx

./apps/web/package.json
{
  "dependencies": {
    "next": "latest",
    "@repo/ui": "workspace:*"
  }
}

Turborepo automatically understands internal package relationships from package manifests. The internal-package guide states that internal packages are the building blocks of the workspace and that Turborepo creates a Package Graph from dependency declarations. That means dependency boundaries are not just documentation for humans; they affect how Turborepo decides which packages depend on which other packages. If a web application consumes a math package, the web package should declare that dependency so builds, tests, and development workflows can be ordered and filtered with the correct graph semantics.

Sources: apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx

Installation Responsibilities and Package Manager Expectations

Turborepo does not replace pnpm, npm, Yarn, or Bun. Use your package manager to add dependencies, update dependency versions, maintain workspace metadata, and write the lockfile. The documentation’s installation examples intentionally use package-manager commands rather than Turborepo commands. That division of responsibility matters for teams: package managers own dependency resolution and the physical installation strategy, including where modules are placed, while Turborepo owns high-performance execution of scripts across the package graph. Keeping those responsibilities separate prevents confusing dependency installation with task scheduling.

Sources: apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx

When adding a tool such as a test runner to multiple packages, install it in each package that directly uses it. The guide gives package-manager-specific examples for adding a development dependency across selected workspaces. The commands differ because package managers expose different workspace selection syntax, but the end state is the same: each target package’s manifest records the dependency. This is especially useful in a large repository where one package may need a test dependency, another may not, and a third may need a different version because it is maintained by another team.

Sources: apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx

Terminal
pnpm add jest --save-dev --recursive --filter=web --filter=@repo/ui --filter=docs
yarn workspace web add jest --dev
npm install jest --workspace=web --workspace=@repo/ui --save-dev
cd apps/web && bun install jest --dev

The lockfile should be treated as shared workspace state produced by the package manager. Although the supplied guide excerpt focuses on where dependencies are declared, the broader workflow implies that consistent installs are foundational for reliable task execution. Turborepo’s caching guide assumes tasks are deterministic with respect to known inputs. Dependency manifests and lockfiles are part of the practical input set that make that determinism possible. If dependency versions drift without being captured by the package manager’s files, developers and CI may run the same task name against different dependency trees.

Sources: apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx

How Dependency Boundaries Affect Tasks and Caching

Dependency boundaries become important when configuring tasks. The task guide explains that Turborepo searches packages for scripts matching task names, and that task relationships can be expressed in the root configuration. A common build configuration uses dependency-aware ordering so libraries build before applications that consume them. That behavior depends on the Package Graph, which comes from package relationships. If internal dependencies are declared accurately, a build can run in the expected order; if they are hidden or only implied, the task graph loses the information needed to schedule work safely.

Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx

Caching adds another reason to keep dependencies explicit. The caching guide describes Turborepo restoring results from a local cache when the fingerprint for a task’s known inputs has already been seen. It also warns that tasks should be deterministic: given the inputs Turborepo knows about, the task should not produce different outputs. Dependency declarations and task inputs are therefore part of the same discipline. A package that imports undeclared code, relies on an untracked version, or reads files outside the expected boundary can make cache behavior harder to reason about.

Sources: apps/docs/content/docs/crafting-your-repository/caching.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx

For development tasks, the same boundaries help developers run only the work they need. The developing-applications guide shows a long-running development task marked as not cacheable and persistent, and it also shows filtering a development run to a specific application. That filter works against the package graph, so accurate dependencies help the selected application bring along the relevant package context. Development servers, test watchers, and setup scripts are often interactive, but they still benefit from the same workspace structure that production builds and CI tasks use.

Sources: apps/docs/content/docs/crafting-your-repository/developing-applications.mdx

CI and Team Workflow Implications

In CI, dependency management should produce repeatable installs before Turborepo runs tasks. The CI guide focuses on speeding up builds, lints, tests, and other pipeline work with parallel execution and Remote Caching. It also documents the remote cache environment variables needed for CI to access shared artifacts. Those optimizations assume the pipeline is running against a coherent workspace install. When package manifests and lockfiles are accurate, CI can install dependencies once, run task commands using the same mental model as local development, and share cache artifacts across machines.

Sources: apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx

Filtering in CI depends on trustworthy workspace metadata. The CI guide describes filtering tasks by packages, directories, Git history, and affected work. These features are most useful when package boundaries reflect real imports and when every package declares its dependencies. For example, a package-specific build or test filter should select the work associated with a changed entry point and its graph relationships. If dependencies are centralized at the root merely for convenience, a human might still know what imports what, but automation has less useful package-level information to operate on.

Sources: apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx, apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx

Practical Checklist

Use this checklist when reviewing a workspace dependency change. First, identify the package that imports the code or runs the tool. Second, add the dependency to that package’s manifest with the package-manager syntax used by the repository. Third, keep internal package names stable because those names become the import specifiers used by other packages. Fourth, commit package-manager metadata such as manifest and lockfile changes together. Finally, confirm that task configuration still reflects the intended order for builds, tests, setup tasks, and persistent development workflows.

Sources: apps/docs/content/docs/crafting-your-repository/managing-dependencies.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/developing-applications.mdx

A good next step is to create or review an internal package, then inspect the task configuration that builds it before dependent applications. After that, run the relevant local task and consider how the same command behaves in CI with Remote Caching enabled. Read the internal-package and configuring-tasks pages together with this guide: dependency declarations define the workspace relationships, task configuration turns those relationships into execution order, and caching uses the resulting inputs and outputs to avoid repeating work.

Sources: apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx