Package and Task Graph
Purpose and Scope
Turborepo understands a workspace in two connected layers. The package graph describes which packages depend on which other packages, and the task graph describes which work should run before other work. This distinction matters because a monorepo is not only a folder tree; it is a set of installable packages, scripts, and dependency relationships that can be scheduled safely. When the workspace is modeled correctly, Turborepo can run builds, checks, and development tasks in dependency-aware order instead of relying on hand-written scripts that accidentally skip required upstream work.
Sources: apps/docs/content/docs/core-concepts/package-and-task-graph.mdx, apps/docs/content/docs/core-concepts/package-types.mdx
The core concept page frames package and task graphs as foundational material for configuring and running Turborepo. Package relationships come from package manager workspace metadata and package manifests, while task relationships are expressed in the root configuration. A reader should treat this page as the bridge between repository structure and task configuration: first make packages discoverable, then define how scripts relate. The result is a directed model that Turborepo can evaluate for ordering, caching, and visualization, including cases where dependencies are nested or where an intermediate package has no matching script.
Sources: apps/docs/content/docs/core-concepts/package-and-task-graph.mdx, apps/docs/content/docs/core-concepts/internal-packages.mdx
Relevant Source Files
- apps/docs/content/docs/core-concepts/package-and-task-graph.mdx — Defines the Package Graph and Task Graph concepts, explains directed acyclic graphs, shows the
buildand^buildexample, and introduces transit nodes. - apps/docs/content/docs/core-concepts/package-types.mdx — Defines application packages and library packages, including the recommendation that applications normally sit at the end of the package graph.
- apps/docs/content/docs/messages/package-task-in-single-package-workspace.mdx — Explains why package-scoped task names are invalid in a single-package workspace and shows the unscoped replacement.
- apps/docs/content/docs/messages/unnecessary-package-task-syntax.mdx — Explains why package-level
turbo.jsonfiles should use local task names rather thanpackage#tasksyntax. - apps/docs/content/docs/core-concepts/internal-packages.mdx — Explains how internal packages are installed through workspace dependency syntax and how they participate in dependency relationships.
- apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx — Shows that adding a package manifest makes an internal package discoverable and lets Turborepo infer package graph edges from dependencies.
Package Graph
The package graph is created from the workspace that your package manager sees. Internal packages are installed into other packages with the same mental model as external dependencies, except the version range uses workspace-aware syntax where appropriate. For example, an application can depend on a shared user interface package, and Turborepo will use that relationship as part of its understanding of the repository. The important point is that package graph edges are not invented in task configuration; they come from the package manager and package manifests that define what each package consumes.
Sources: apps/docs/content/docs/core-concepts/internal-packages.mdx, apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx
Package type influences how the graph should be designed. Application packages are deployable outputs such as web applications, command line programs, or framework applications commonly placed under an applications directory. Library packages hold shared code that supports those applications and are often internal packages. The documentation recommends that applications usually be the ends of the package graph rather than dependencies of other packages. That shape helps continuous integration and deployment workflows finish at the deployable nodes, while libraries remain reusable building blocks feeding those final artifacts.
Sources: apps/docs/content/docs/core-concepts/package-types.mdx
Creating an internal package is therefore not only a code-sharing step; it changes the graph Turborepo can reason about. The guide for creating an internal package starts by adding a directory and then adding a package manifest with a name, scripts, exports, and dependencies. The package name becomes the import name that other packages use. Once another package declares that internal dependency, Turborepo can infer a package graph edge under the hood. That edge later becomes relevant when a task uses dependency-aware syntax such as asking upstream packages to build first.
Sources: apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx
Task Graph
The task graph is configured in the workspace configuration rather than inferred solely from package dependencies. In the root task configuration, a task can declare which other tasks must complete before it. The docs describe this as a directed acyclic graph: tasks are nodes, and dependencies between tasks are directed edges. If one task points to another, the first task depends on the second. This gives Turborepo enough structure to decide execution order without turning every package script into a bespoke orchestration command.
Sources: apps/docs/content/docs/core-concepts/package-and-task-graph.mdx
A common example is a build task that depends on upstream builds. In the documented scenario, an application depends on two internal packages, and the build task is configured to depend on the same task in dependency packages. When the build is requested, Turborepo combines package graph knowledge with task configuration. It understands that the application’s build is downstream of the library package builds, so it can schedule the package builds before the application build. This is why correct workspace dependencies and correct task configuration need to be maintained together.
Sources: apps/docs/content/docs/core-concepts/package-and-task-graph.mdx
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}The graph model also supports visualization. The package-and-task-graph documentation notes that the run command can generate a visualization of task relationships through the graph flag. That is useful when a repository grows beyond the point where a person can inspect every package relationship by memory. Instead of guessing why a particular task waits for another task, the generated graph shows the nodes and edges that Turborepo derived from configuration and repository structure. Use that visualization when validating a new package layout, reviewing task configuration, or debugging unexpected scheduling behavior.
Sources: apps/docs/content/docs/core-concepts/package-and-task-graph.mdx
Transit Nodes and Nested Dependencies
Transit nodes explain an important edge case in dependency-aware scheduling. Consider an application that depends on a shared package, while that shared package depends on a deeper core package. If the middle package does not define the requested script, Turborepo still has to understand the path through the package graph. The documentation describes this as a transit node: the middle package participates in the dependency path even when it has no matching task to execute. That prevents the deeper dependency from disappearing just because an intermediate package lacks a script.
Sources: apps/docs/content/docs/core-concepts/package-and-task-graph.mdx
This behavior is easiest to understand by separating packages from tasks. A package can be a dependency even when it does not have a particular script. A task graph for a specific run only contains executable task nodes where scripts exist, but the package graph still explains how Turborepo reaches transitive dependencies. When a build depends on upstream builds, Turborepo can walk through packages without build scripts to find dependencies that do have build scripts. That keeps dependency ordering correct without forcing every package to define every possible task.
Sources: apps/docs/content/docs/core-concepts/package-and-task-graph.mdx
Task Naming and Configuration Boundaries
Task names must match the scope where they are declared. In a single-package workspace, there is no set of multiple workspace packages, so declaring a package-scoped task name is invalid. The troubleshooting documentation shows a configuration using a package prefix for build and recommends removing the package name so the task is simply named build. This rule follows directly from the graph model: if there is only one package, package qualification cannot identify a distinct workspace node and should not be used to configure the task.
Sources: apps/docs/content/docs/messages/package-task-in-single-package-workspace.mdx
The same idea appears in package-level workspace configurations. Turborepo supports additional configuration files inside package directories to override or extend the root configuration for that package. Inside that package-local context, the package is already known, so the task key should contain only the task name. The unnecessary package task syntax message shows a package-level configuration that uses a prefixed build task and instructs readers to remove the package prefix. This keeps root-level graph configuration and package-local overrides from mixing two different scopes.
Sources: apps/docs/content/docs/messages/unnecessary-package-task-syntax.mdx
{
"tasks": {
"build": {
"dependsOn": ["lint"]
}
}
}System-to-Code Mapping
| Concept | Where it is defined | Practical effect |
|---|---|---|
| Package Graph | Package manager workspace and package.json dependencies | Tells Turborepo which packages depend on other packages. |
| Internal Package | Workspace library installed by another package | Creates reusable code and graph edges inside the repository. |
| Application Package | Deployable package, usually at the end of the graph | Often becomes the final node for CI or deployment workflows. |
| Task Graph | turbo.json task relationships | Tells Turborepo which scripts must run before others. |
| Transit Node | Package dependency path without a matching task | Preserves transitive scheduling when an intermediate package lacks a script. |
| Package-local task key | Package-level turbo.json | Uses the local task name because the package context is already known. |
Practical Workflow
Start by making packages explicit. Each internal package should have a package manifest with a stable name, and consumers should declare it as a dependency using the package manager’s workspace mechanism. Next, classify packages by role: applications should normally consume libraries, while libraries should avoid depending on deployable applications unless there is a specific reason such as end-to-end test awareness. Then configure tasks in the root workspace configuration, using dependency-aware task relationships for operations that must respect upstream packages. Finally, validate the resulting task graph with visualization when behavior is not obvious.
Sources: apps/docs/content/docs/core-concepts/package-types.mdx, apps/docs/content/docs/core-concepts/internal-packages.mdx, apps/docs/content/docs/core-concepts/package-and-task-graph.mdx
When troubleshooting, ask whether the problem is about package discovery, package relationships, task relationships, or task naming scope. Missing package graph edges usually point back to workspace structure or package dependencies. Unexpected task order usually points back to task configuration. Errors about scoped task syntax usually mean the task key was written for the wrong configuration context. This diagnostic split helps avoid changing unrelated files: fix package manifests for package graph problems, fix task definitions for task graph problems, and fix task keys when Turborepo reports package-scoping mistakes.
Sources: apps/docs/content/docs/messages/package-task-in-single-package-workspace.mdx, apps/docs/content/docs/messages/unnecessary-package-task-syntax.mdx
Next Steps
After understanding this page, read the internal packages material before designing a shared library strategy, then move to task configuration guidance to define inputs, outputs, caching, and dependencies with more precision. If you are creating a new library, follow the internal package creation guide so the package is discoverable and importable before relying on it in task execution. For existing repositories, inspect package manifests first, then review task keys and dependency declarations. That order mirrors how Turborepo builds understanding: package graph first, task graph second, execution plan after both are known.