Vite and Turborepo can build multiple apps in a monorepo, but the build process isn’t as simple as just running vite build in each app’s directory.

Here’s how you can set up your monorepo to build multiple Vite applications using Turborepo for efficient orchestration.

First, let’s assume a monorepo structure like this:

my-monorepo/
├── apps/
│   ├── app1/
│   │   ├── src/
│   │   ├── index.html
│   │   ├── vite.config.js
│   │   └── package.json
│   └── app2/
│       ├── src/
│       ├── index.html
│       ├── vite.config.js
│       └── package.json
├── packages/
│   └── ui/
│       ├── src/
│       ├── package.json
├── turbo.json
└── package.json

You’ll need to install vite and turborepo as dev dependencies in your root package.json.

// package.json (root)
{
  "name": "my-monorepo",
  "private": true,
  "version": "1.0.0",
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev"
  },
  "devDependencies": {
    "turbo": "^1.10.3",
    "vite": "^4.4.9"
  }
}

Now, let’s configure turbo.json to manage the build tasks. Turborepo uses a turbo.json file at the root of your monorepo to define tasks and their dependencies.

// turbo.json
{
  "$schema": "https://turborepo.org/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

The build pipeline is set up to depend on any build tasks in its dependencies (^build). This means if packages/ui has a build script, apps/app1’s build will wait for packages/ui’s build to complete. The outputs field tells Turborepo where to find the build artifacts.

For each Vite app (e.g., apps/app1), you’ll need a package.json with a build script that invokes Vite.

// apps/app1/package.json
{
  "name": "app1",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "ui": "workspace:*" // Assuming 'ui' is in packages/ui
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.0.4",
    "vite": "^4.4.9"
  }
}

Notice the ui: "workspace:*" dependency. This allows your apps to directly reference packages within the monorepo. When you run turbo run build, Turborepo will:

  1. Identify buildable packages: It scans your monorepo for package.json files containing a build script.
  2. Resolve dependencies: It understands the dependency graph based on package.json dependencies and devDependencies (especially workspace:* references).
  3. Execute in parallel: It runs the build scripts for packages that have no outstanding dependencies first.
  4. Cache results: For build tasks, Turborepo caches the output (dist/**) so subsequent builds are faster if nothing has changed.
  5. Handle multiple apps: It will execute the build script for apps/app1 and apps/app2 (and any other apps/packages with a build script) respecting their dependencies.

To build all your applications, you simply run:

turbo run build

Turborepo will intelligently determine the order of operations, build packages that are shared first, and then build the applications that depend on them.

If you have a shared UI library in packages/ui that app1 and app2 depend on, the build process will look something like this:

  1. packages/ui builds.
  2. apps/app1 builds (which depends on packages/ui).
  3. apps/app2 builds (which also depends on packages/ui).

Turborepo handles the dependency resolution and execution order automatically.

The dev script is often configured with cache: false and persistent: true in turbo.json because development servers are typically long-running and don’t produce cacheable artifacts. Running turbo run dev will start the development servers for all your apps.

turbo run dev

This command will start the Vite development server for app1 and app2 concurrently.

The truly surprising thing about this setup is how Turborepo’s caching mechanism works with Vite. It doesn’t just cache the final output of vite build; it caches the build artifacts at each level of the dependency graph. If you change a component in packages/ui and run turbo run build again, only packages/ui and the apps that directly depend on it will be rebuilt. Apps that don’t use packages/ui will be skipped entirely, making incremental builds incredibly fast.

The next concept you’ll want to explore is how to manage environment variables across your Vite apps within the monorepo, especially when using Turborepo to orchestrate builds.

Want structured learning?

Take the full Vite course →