Deploying multiple distinct applications from a single Git repository on Vercel is surprisingly straightforward, but the magic really happens when you realize Vercel doesn’t treat your monorepo as a single deployable unit – it intelligently identifies and builds each project independently.

Let’s see this in action. Imagine a monorepo with two Vercel projects: a frontend app and an API.

/
├── apps/
│   ├── web/          <-- Project 1 (frontend)
│   │   ├── next.config.js
│   │   ├── package.json
│   │   └── ...
│   └── api/          <-- Project 2 (backend)
│       ├── next.config.js
│       ├── package.json
│       └── ...
├── packages/
│   ├── ui/
│   │   ├── package.json
│   │   └── ...
│   └── utils/
│       ├── package.json
│       └── ...
├── package.json
└── vercel.json       <-- Root config

When you push to Vercel, it scans your repository. It looks for package.json files with a build script and, crucially, next.config.js (or similar framework-specific configs) within subdirectories. Vercel’s build system then spins up separate build environments for apps/web and apps/api. It analyzes dependencies, runs npm run build (or yarn build) in each detected project directory, and deploys them as distinct Vercel deployments, each with its own unique URL.

The core problem this solves is managing complexity and reducing overhead. Instead of juggling multiple repositories, each with its own CI/CD pipelines, dependencies, and deployment configurations, you centralize everything. This leads to faster onboarding for new developers, easier dependency management across projects (e.g., shared UI components in packages/ui), and a simplified Git workflow.

Internally, Vercel uses a sophisticated file-watching and project detection mechanism. When a Git push occurs, Vercel analyzes the changed files. If changes are detected within the apps/web directory (and its dependencies), it triggers a build for that project. Similarly, changes in apps/api trigger its build. This selective building ensures that only affected projects are rebuilt and redeployed, saving significant time and resources.

The key to making this work is Vercel’s configuration. You can explicitly tell Vercel which directories contain your projects and how they should be built. This is done in the vercel.json file at the root of your monorepo.

Here’s a typical vercel.json for the structure above:

{
  "projects": [
    {
      "name": "web-app",
      "rootDirectory": "apps/web",
      "buildCommand": "npm run build",
      "outputDirectory": "dist" // Or ".next" if using Next.js
    },
    {
      "name": "api-service",
      "rootDirectory": "apps/api",
      "buildCommand": "npm run build",
      "outputDirectory": "dist" // Or ".next" if using Next.js
    }
  ]
}

In this configuration:

  • "name": A human-readable identifier for the project within Vercel.
  • "rootDirectory": This is the crucial part – it tells Vercel where to find the project’s source code and package.json.
  • "buildCommand": The command Vercel should execute to build the project.
  • "outputDirectory": Where the build artifacts are placed. For Next.js, this is typically .next. For other frameworks, it might be dist or build.

Vercel intelligently infers many of these settings if you don’t explicitly provide them, especially for popular frameworks like Next.js. It will look for package.json files, detect next.config.js, and assume standard build commands and output directories. However, explicitly defining them in vercel.json provides clarity and ensures Vercel builds exactly what you intend.

The real power comes when you leverage shared packages. If your apps/web and apps/api both depend on packages/ui and packages/utils, Vercel’s build process will hoist these dependencies during the build. The monorepo’s root package.json (or a workspace-aware manager like Yarn Workspaces or pnpm) defines these relationships. When Vercel builds apps/web, it installs dependencies, including those from packages/ui and packages/utils, making them available. The same happens for apps/api. This means you can share code, types, and even components across your independently deployable projects without duplication.

A common point of confusion is how Vercel handles monorepo root configurations versus project-specific configurations. While you can have a vercel.json at the root, it’s often used for global settings or to define multiple projects as shown above. Each individual project directory (e.g., apps/web/package.json) can still have its own specific build scripts or environment variables that Vercel will respect when building that particular project. Vercel’s build system merges these configurations, prioritizing project-specific settings where they conflict.

The next step in mastering monorepos on Vercel is understanding how to manage environment variables for each distinct project, especially when they differ significantly.

Want structured learning?

Take the full Vercel course →