Setting Up an Nx Monorepo: A Step-by-Step Guide | AI Hub Blog | AI Hub
Tutorial
Setting Up an Nx Monorepo: A Step-by-Step Guide
H
Hasan
AI Research Lead
June 11, 2024
10 min read
Generated by AI-Hub
Boost your development workflow by mastering Nx monorepos. Learn how to set up, manage dependencies, orchestrate parallel tasks, and establish optimized CI/CD pipelines in this comprehensive, production-ready guide.
Setting Up an Nx Monorepo: A Step-by-Step Guide
In the world of modern software engineering, managing multiple interconnected projects efficiently is critical to maintaining high productivity, ensuring consistency, and keeping code quality top-notch. Traditional multi-repo architectures (polyrepos) often lead to major headaches, including dependency drift, version mismatches, code duplication, and highly fragmented continuous integration (CI) pipelines.
Monorepos—single repositories hosting multiple projects—have emerged as the leading standard to solve these organizational bottlenecks. However, without the right tooling, a monorepo can quickly devolve into a chaotic "monolith" with slow build times and tangled dependencies.
This is where Nx comes in. Developed by Nrwl, Nx is a smart, fast, and highly extensible build system designed to manage monorepos of any size. In this comprehensive, step-by-step guide, we will walk you through setting up an Nx monorepo from scratch, explore core monorepo architectures, write shared code, optimize tasks, and prepare a production-grade CI/CD pipeline.
Watch the Video Tutorial
Deepen Your Knowledge
Continue exploring related insights and research in Tutorial.
Monorepo Architecture: Integrated vs. Package-Based
Before diving into the code, it is vital to understand the two primary operational styles Nx offers. Your choice determines how dependencies and configurations are structured in your repository.
Feature
Integrated Repo (Recommended for Greenfields)
Package-Based Repo (Recommended for Migrations)
Dependency Management
Single package.json at the root. All projects share the same package versions.
Multiple package.json files. Projects declare their own dependencies (like npm workspaces).
Configuration
Handled by Nx generators. Tooling configurations (TypeScript, Jest, ESLint) are centralized.
Traditional config files per project. Nx acts purely as an orchestrator and cache manager.
Code Sharing
Immediate via TypeScript path mapping configured automatically by generators.
Via package reference inside localized dependency declarations.
Learning Curve
Moderate; requires adopting Nx-specific generators and conventions.
Extremely low; builds on standard package manager workspaces.
For this tutorial, we will focus on the Integrated model, as it provides the most cohesive experience, complete with automated code generators and optimized dependency management.
Why Choose Nx for Your Monorepo?
Nx stands out in the build system space due to several key features:
Interactive Project Graph: Dynamically analyze and visualize your project dependencies. Nx reads your TypeScript imports directly to understand which application depends on which library, rather than relying on manual, error-prone configuration mappings.
Smart Computation Caching: Nx stores the output of build, test, and lint tasks locally and remotely. If a task has been executed before with the same inputs, Nx skips running it entirely and instantly restores the cached terminal outputs and files.
Task Orchestration: Run tasks concurrently across different CPU cores or distribute them dynamically across multiple agents in your CI pipeline (via Nx Cloud).
Generators & Executors: Scaffold standardized libraries, applications, components, and configuration files using a single CLI command, maintaining architectural consistency across massive teams.
Prerequisites
To follow along with this tutorial, ensure your local development environment meets the following requirements:
Node.js: Version 18.x or 20.x (LTS recommended).
Package Manager: npm, Yarn, pnpm, or Bun. We will use npm for this guide.
Git: Installed and initialized locally (essential for Nx to compute modified files via the affected engine).
Step 1: Install Nx CLI
While you can run all commands directly via npx, installing the global nx CLI can speed up terminal execution and offer native autocomplete:
npm install -g nx
If you prefer to keep your global environment clean, you can prepend all commands in this tutorial with npx (e.g., npx nx g ... instead of nx g ...).
Step 2: Create a New Nx Workspace
Let's generate a clean workspace. Run the following command in your terminal:
npx create-nx-workspace@latest my-workspace
During initialization, the CLI will prompt you with several structural questions. For an optimized integrated workspace, choose the following configurations:
Which stack would you like to use? Choose React (or None/integrated if you want a blank slate to customize yourself).
What framework would you like to use? Choose Next.js or Vite (for this guide, we'll choose standard React with Vite).
Application name: Enter app1.
Default stylesheet format: Choose Sass or CSS (we'll use CSS for simplicity).
Enable distributed caching? Yes (to set up a free tier of Nx Cloud caching for faster builds).
Navigate to your project folder:
cd my-workspace
Exploring the Project Structure
Open the workspace in your favorite code editor (e.g., VS Code). You will see the following directory layout:
my-workspace/
├── apps/
│ ├── app1/ # Our first generated application
│ └── app1-e2e/ # End-to-end testing suite for app1
├── libs/ # Shared libraries and components go here
├── nx.json # Nx configuration and task definitions
├── package.json # Centralized dependencies
├── tsconfig.base.json # Base TypeScript configuration
└── eslint.config.js # Centralized ESLint configuration
Step 3: Adding a Second Application
One of the core strengths of a monorepo is hosting multiple apps under the same umbrella. Let's add a second React application to see how they can live side-by-side.
We must first install the @nx/react plugin to access its generator commands:
This creates a library structure under libs/shared-utils. Check your tsconfig.base.json file; you will notice that Nx has automatically added a path mapping alias:
Let's write a simple helper function inside the newly created library. Open libs/shared-utils/src/lib/shared-utils.ts and replace its contents with this clean currency formatter:
export function formatCurrency(amount: number, currency = 'USD'): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
}).format(amount);
}
Export this function in the main entry point, libs/shared-utils/src/index.ts:
export * from './lib/shared-utils';
Consuming the Library in an Application
We can now import this function directly into our applications using our clean import alias. Let's modify apps/app1/src/app/app.tsx:
import React from 'react';
import { formatCurrency } from '@my-workspace/shared-utils';
export function App() {
const price = 49.99;
return (
<div className="app-container">
<h1>Welcome to App 1</h1>
<p>The subscription cost is: <strong>{formatCurrency(price)}</strong></p>
</div>
);
}
export default App;
Run your development server for app1 to verify everything compiles:
Nx excels at running tasks efficiently. Let's explore how caching and parallel task execution work in practice.
Running Tasks for a Single Project
To compile app1:
nx build app1
Run this command a second time. You will notice that the execution completes instantly (often in under 15ms) with a terminal message stating: [local cache]. Nx recognized that no source files or dependencies were modified, and immediately retrieved the output from the cache folder (.nx/cache).
Running Multiple Tasks Simultaneously
To run builds for all applications and libraries inside your workspace in parallel:
nx run-many --target=build
To run specific targets on specific projects:
nx run-many --target=test --projects=app1,app2
Configuring Target Pipelines (nx.json)
Inside nx.json, you can define relationships between tasks. For example, ensuring that a project's dependencies are always built before the project itself is built:
Here, the ^build syntax instructs Nx to run the build target on all upstream dependencies (like our shared-utils library) before executing the build on the parent app.
Step 6: Visualizing the Monorepo Structure
As your monorepo scales to dozens of apps and libraries, maintaining a mental map of relationships becomes impossible. Nx resolves this with a visual analyzer.
Run the dependency graph command:
nx graph
This command compiles your workspace graph and spins up an interactive visualization in your default web browser. You can filter paths, trace how modifications in shared-utils impact app1 and app2, and debug circular dependency issues in real time.
Step 7: Configuring an Optimized CI/CD Pipeline
Running tests and builds across an entire monorepo on every commit is highly inefficient. Nx solves this using affected commands, which analyze your Git tree to isolate and run tasks only for the projects impacted by the current branch change.
Here is a production-ready GitHub Actions workflow (.github/workflows/ci.yml) utilizing nx affected and setting up proper Git history references:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# We need to fetch all history so that Nx can calculate changes relative to main
fetch-depth: 0
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Initialize Git Comparison SHAs
run: npx nx-set-shas
- name: Lint Affected Projects
run: npx nx affected -t lint
- name: Run Affected Tests
run: npx nx affected -t test
- name: Build Affected Applications
run: npx nx affected -t build
Why nx-set-shas is Essential
The npx nx-set-shas step registers the commit hash of the last successful CI run. This allows Nx to determine the exact delta of code changes on your current branch and accurately skip runs for unmodified modules, saving massive pipeline minutes and minimizing CI costs.
Best Practices for Maintaining an Nx Monorepo
To ensure your monorepo scales cleanly as teams and projects grow, adopt these architectural patterns:
Enforce Boundary Rules (eslint-plugin-nx): Prevent architectural regression. Ensure that frontend apps do not directly import server-side modules by defining tagging rules. Assign tags (e.g., type:app, type:lib, scope:shared) inside each project's metadata configuration (project.json), then restrict imports in your global ESLint rules.
Keep Libraries Small and Granular: Avoid generating massive general-purpose libraries (like utils). Instead, break them down by domain or feature (e.g., libs/auth/utils, libs/payment/components).
Use Nx Migrate regularly: Keeping build systems and dependent libraries up to date is historically painful. Run nx migrate latest to analyze your workspaces, upgrade package versions, and automatically rewrite outdated configuration files via specialized migration scripts.
Frequently Asked Questions (FAQ)
Is Nx only for Angular?
Historically, Nx was created by former Angular Core members and tightly integrated with it. Today, Nx is completely framework-agnostic. It features tier-one support for React, Next.js, Vue, Svelte, Nuxt, NestJS, Express, and even non-JavaScript ecosystems like Rust and Go.
Can I use Nx with monorepos built via npm/pnpm/yarn workspaces?
Yes! This is known as Package-Based mode. You can drop Nx into any pre-existing monorepo. By simply adding an nx.json file, you can immediately gain access to ultra-fast task running, parallel execution, and computation caching without rearranging your folder structure.
What is Nx Cloud and is it required?
Nx Cloud handles remote caching and distributed task execution. While the core build system runs perfectly and for free locally, Nx Cloud accelerates CI builds by allowing developers to share cache hits globally. If your peer built a branch locally, your CI machine (and other developers) will download the compiled assets instead of re-running the build. It is completely optional but highly recommended for fast-moving teams.
Conclusion
Setting up an Nx monorepos provides a powerful, future-proof platform for managing complex project architectures. By following this guide, you have established an optimized codebase where dependencies are clear, shared modules compile via mapped aliases, computation outputs cache beautifully, and CI tasks only target what is strictly necessary. As your codebase grows, your developer velocity will remain fast and predictable.
How to Use AI for Content Creation: Best Practices
Discover how to leverage AI for content creation without losing your brand's voice or hurting your SEO. This comprehensive guide covers the Human-in-the-Loop workflow, Python integration, style prompts, and E-E-A-T compliance.