---
title: Package Configurations
description: Learn how to use Package Configurations to bring greater task flexibility to your monorepo's package.
---

import { Callout } from '#components/callout';
import { ExperimentalBadge } from '#components/experimental-badge';

Many monorepos can declare a `turbo.json` in the root directory with a
[task description](/docs/reference/configuration#tasks) that applies to all packages. But, sometimes, a monorepo can contain packages that need to configure their tasks differently.

To accommodate this, Turborepo enables you to extend the root configuration with a `turbo.json` in any package. This flexibility enables a more diverse set of apps and packages to co-exist in a Workspace, and allows package owners to maintain specialized tasks and configuration without affecting other apps and packages of the monorepo.

## How it works

To override the configuration for any task defined in the root `turbo.json`, add
a `turbo.json` file in any package of your monorepo with a top-level `extends`
key:

```jsonc title="./apps/my-app/turbo.json"
{
  "extends": ["//"],
  "tasks": {
    "build": {
      // Custom configuration for the build task in this package
    },
    "special-task": {} // New task specific to this package
  }
}
```

<Callout>
  For now, the only valid value for the `extends` key is `["//"]`. `//` is a
  special name used to identify the root directory of the monorepo.
</Callout>

Configuration in a package can override any of [the configurations for a
task](/docs/reference/configuration#defining-tasks). Any keys that are not included are inherited
from the extended `turbo.json`.

## Examples

### Different frameworks in one Workspace

Let's say your monorepo has multiple [Next.js](https://nextjs.org) apps, and one [SvelteKit](https://kit.svelte.dev)
app. Both frameworks create their build output with a `build` script in their
respective `package.json`. You _could_ configure Turborepo to run these tasks
with a single `turbo.json` at the root like this:

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    }
  }
}
```

Notice that both `.next/**` and `.svelte-kit/**` need to be specified as
[`outputs`](/docs/reference/configuration#outputs), even though Next.js apps do not generate a `.svelte-kit` directory, and
vice versa.

With Package Configurations, you can instead add custom
configuration in the SvelteKit package in `apps/my-svelte-kit-app/turbo.json`:

```jsonc title="./apps/my-svelte-kit-app/turbo.json"
{
  "extends": ["//"],
  "tasks": {
    "build": {
      "outputs": [".svelte-kit/**"]
    }
  }
}
```

and remove the SvelteKit-specific [`outputs`](/docs/reference/configuration#outputs) from the root configuration:

```diff title="./turbo.json"
{
  "tasks": {
    "build": {
-      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
+      "outputs": [".next/**", "!.next/cache/**"]
    }
  }
}
```

This not only makes each configuration easier to read, it puts the configuration
closer to where it is used.

### Specialized tasks

In another example, say that the `build` task in one package `dependsOn` a
`compile` task. You could universally declare it as `dependsOn: ["compile"]`.
This means that your root `turbo.json` has to have an empty `compile` task
entry:

```json title="./turbo.json"
{
  "tasks": {
    "build": {
      "dependsOn": ["compile"]
    },
    "compile": {}
  }
}
```

With Package Configurations, you can move that `compile` task into the
`apps/my-custom-app/turbo.json`,

```json title="./apps/my-app/turbo.json"
{
  "extends": ["//"],
  "tasks": {
    "build": {
      "dependsOn": ["compile"]
    },
    "compile": {}
  }
}
```

and remove it from the root:

```diff title="./turbo.json"
{
  "tasks": {
+    "build": {}
-    "build": {
-      "dependsOn": ["compile"]
-    },
-    "compile": {}
  }
}
```

Now, the owners of `my-app`, can have full ownership over their `build` task,
but continue to inherit any other tasks defined at the root.

## Comparison to package-specific tasks

At first glance, Package Configurations may sound a lot like the
[`package#task` syntax](/docs/crafting-your-repository/configuring-tasks#depending-on-a-specific-task-in-a-specific-package) in the root `turbo.json`. The features are
similar, but have one significant difference: when you declare a package-specific task
in the root `turbo.json`, it _completely_ overwrites the baseline task
configuration. With a Package Configuration, the task configuration is merged
instead.

Consider the example of the monorepo with multiple Next.js apps and a Sveltekit
app again. With a package-specific task, you might configure your root
`turbo.json` like this:

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      "outputLogs": "hash-only",
      "inputs": ["src/**"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "my-sveltekit-app#build": {
      "outputLogs": "hash-only", // must duplicate this
      "inputs": ["src/**"], // must duplicate this
      "outputs": [".svelte-kit/**"]
    }
  }
}
```

In this example, `my-sveltekit-app#build` completely overwrites `build` for the
Sveltekit app, so `outputLogs` and `inputs` also need to be duplicated.

With Package Configurations, `outputLogs` and `inputs` are inherited, so
you don't need to duplicate them. You only need to override `outputs` in
`my-sveltekit-app` config.

<Callout type="info">
  Although there are no plans to remove package-specific task configurations, we
  expect that Package Configurations can be used for most use cases instead.
</Callout>

## Boundaries Tags <ExperimentalBadge>Experimental</ExperimentalBadge>

Package Configurations are also used to declare Tags for Boundaries. To do so, add a `tags` field to your `turbo.json`:

```diff title="./apps/my-app/turbo.json"
{
+ "tags": ["my-tag"],
  "extends": ["//"],
  "tasks": {
    "build": {
      "dependsOn": ["compile"]
    },
    "compile": {}
  }
}
```

From there, you can define rules for which dependencies or dependents a tag can have. Check out the [Boundaries documentation](/docs/reference/boundaries#tags) for more details.

## Limitations

Although the general idea is the same as the root `turbo.json`, Package
Configurations come with a set of guardrails that can prevent packages from creating
potentially confusing situations.

### Package Configurations cannot use [the `workspace#task` syntax](/docs/crafting-your-repository/running-tasks) as task entries

The `package` is inferred based on the location of the configuration, and it is
not possible to change configuration for another package. For example, in a
Package Configuration for `my-nextjs-app`:

```jsonc title="./apps/my-nextjs-app/turbo.json"
{
  "tasks": {
    "my-nextjs-app#build": {
      // ❌ This is not allowed. Even though it's
      // referencing the correct package, "my-nextjs-app"
      // is inferred, and we don't need to specify it again.
      // This syntax also has different behavior, so we do not want to allow it.
      // (see "Comparison to package-specific tasks" section)
    },
    "my-sveltekit-app#build": {
      // ❌ Changing configuration for the "my-sveltekit-app" package
      // from Package Configuration in "my-nextjs-app" is not allowed.
    },
    "build": {
      // ✅ just use the task name!
    }
  }
}
```

Note that the `build` task can still depend on a package-specific task:

```jsonc title="./apps/my-nextjs-app/turbo.json"
{
  "tasks": {
    "build": {
      "dependsOn": ["some-pkg#compile"] // [!code highlight]
    }
  }
}
```

### Package Configurations can only override values in the `tasks` key

It is not possible to override [global configuration](/docs/reference/configuration#global-options) like `globalEnv` or `globalDependencies` in a Package Configuration. Configuration that would need to be altered in a Package Configuration is not truly global and should be configured differently.

### Root turbo.json cannot use the `extends` key

To avoid creating circular dependencies on packages, the root `turbo.json`
cannot extend from anything. The `extends` key will be ignored.

## Troubleshooting

In large monorepos, it can sometimes be difficult to understand how Turborepo is
interpreting your configuration. To help, we've added a `resolvedTaskDefinition`
to the [Dry Run](/docs/reference/run#--dry----dry-run) output. If you run `turbo run build --dry-run`, for example, the
output will include the combination of all `turbo.json` configurations that were
considered before running the `build` task.
