Developing applications

Developing applications in a monorepo unlocks powerful workflows, enabling you to make atomic commits to source control with easy access to code.

Most development tasks are long-running tasks that watch for changes to your code. Turborepo enhances this experience with a powerful terminal UI and other capabilities like:

Configuring development tasks

Defining a development task in turbo.json tells Turborepo that you'll be running a long-lived task. This is useful for things like running a development server, running tests, or building your application.

To register a dev task, add it to your turbo.json with two properties:

./turbo.json
{
  "tasks": {
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
  • "cache": false: Tells Turborepo to not attempt to cache the results of the task. Since this is a development task, you're likely to be making frequent changes to your code, so caching the results is not useful.
  • "persistent": true: Tells Turborepo to keep the task running until you stop it. This key serves as a signal for your terminal UI to treat the task as long-running and interactive. Additionally, it prevents you from accidentally depending on a task that will not exit.

You can now run your dev task to start your development scripts in parallel:

Terminal
turbo dev

Running setup tasks before dev

You may also want to run scripts that set up your development environment or pre-build packages. You can make sure those tasks run before the dev task with dependsOn:

./turbo.json
{
  "tasks": {
    "dev": {
      "cache": false,
      "persistent": true,
      "dependsOn": ["//#dev:setup"]
    },
    "//#dev:setup": {
      "outputs": [".codegen/**"]
    }
  }
}

In this example, we're using a Root Task but you can use the same idea for arbitrary tasks in packages.

Running a specific application

The --filter flag allows you to pick a subset of your Package Graph so you can run your dev task for a specific application and its dependencies:

Terminal
turbo dev --filter=web

Watch Mode

watch reference

Many tools have a built-in watcher, like tsc --watch, that will respond to changes in your source code. However, some don't.

turbo watch adds a dependency-aware watcher to any tool. Changes to source code will follow the Task Graph that you've described in turbo.json, just like all your other tasks.

For example, using a package structure like create-turbo with the following tasks and scripts:

turbo.json
{
  "tasks": {
    "dev": {
      "persistent": true,
      "cache": false
    },
    "lint": {
      "dependsOn": ["^lint"]
    }
  }
}

When you run turbo watch dev lint, you'll see the lint scripts are re-run whenever you make source code changes, despite ESLint not having a built-in watcher. turbo watch is also aware of internal dependencies, so a code change in @repo/ui will re-run the task in both @repo/ui and web.

The Next.js development server in web and the TypeScript Compiler's built-in watcher in @repo/ui will continue to work as usual, since they are marked with persistent.

Limitations

Teardown tasks

In some cases, you may want to run a script when the dev task is stopped. Turborepo is unable to run those teardown scripts when exiting because turbo exits when your dev tasks exit.

Instead, create a turbo dev:teardown script that you run separately after you've exited your primary turbo dev task.

Next steps

Once you have a version of your application that you'd like to deploy, it's time to learn how to configure environment variables in Turborepo.

hours

Total Compute Saved
Get started with
Remote Caching →

On this page