Emerald City·devops·10 min read

Deploying to Vercel from an Nx Monorepo

Step-by-step guide for deploying a Next.js app from an Nx monorepo to Vercel — including the config pitfalls that catch Australian developers by surprise.

Deploying to Vercel from an Nx Monorepo

Monorepos are brilliant for Australian engineering teams with shared component libraries and multiple apps. But deploying from one to Vercel has a few traps.


The Core Challenge

Vercel expects a Next.js project at the root of your repo. In an Nx monorepo, your app is buried at apps/my-app/. You need to tell Vercel where to look.


vercel.json Configuration

Place this at the monorepo root:

{
  "buildCommand": "npx nx build my-app --prod",
  "outputDirectory": "apps/my-app/.next",
  "installCommand": "npm install",
  "framework": "nextjs"
}

Replace my-app with your Nx project name.


next.config.js for Nx

Your Next.js config needs to know about the monorepo root for Turbopack:

const path = require('path');

/** @type {import('next').NextConfig} */
module.exports = {
  experimental: {
    turbopack: {
      root: path.join(__dirname, '../..'), // workspace root
    },
  },
};

Environment Variables

In the Vercel dashboard, environment variables are configured per project. For a monorepo:

  1. Go to your Vercel project settings
  2. Under Environment Variables, add all your vars
  3. Mark server-only vars as Server scope (never Client)

The convention:

  • NEXT_PUBLIC_* → safe for client
  • Everything else → server only

tsconfig Paths

In an Nx monorepo, you'll have a root tsconfig.base.json and per-app tsconfig.json files. Make sure your per-app config inherits from the base:

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "paths": {
      "@my-org/ui": ["../../libs/ui/src/index.ts"],
      "@my-org/ui/*": ["../../libs/ui/src/*"]
    }
  }
}

Vercel's TypeScript compiler will use this during build.


Common Pitfalls

1. serverExternalPackages required for Node-only deps

Some npm packages (like gray-matter) use Node.js APIs that can't be bundled for Turbopack. Add them to next.config.js:

module.exports = {
  serverExternalPackages: ['gray-matter'],
};

2. Dynamic routes must await params in Next.js 16+

// Wrong
export default function Page({ params }: { params: { slug: string } }) {
  const { slug } = params; // ❌ will error in Next.js 16
}

// Correct
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params; // ✅
}

3. Nx build cache not invalidating

If Vercel caches your Nx build incorrectly, add to vercel.json:

{
  "buildCommand": "npx nx build my-app --skip-nx-cache"
}

Preview Environments

Vercel creates a unique URL per Pull Request. This is genuinely one of the best features in modern deployment infrastructure — use it aggressively.

Tip: add a comment to your PR template:

**Vercel Preview**: Check the Vercel bot comment below for a live preview URL.

You're Deploying Like a Canva Engineer Now

Canva, Linktree, and most Sydney-based unicorns run similar setups. The yellow brick road to production runs straight through Vercel.