Star

Nx Monorepo with Angular Libraries: Scale Your Frontend in 2026

Learn how to set up an Nx monorepo with Angular libraries, share components across apps, run affected commands, and speed up CI with remote caching.

RJ
RAMA JONNADA
Senior Frontend Developer with 4+ years of experience in Angular and modern web technologies.

TL;DR: An Nx monorepo lets you share Angular libraries — components, services, utilities — across multiple apps in one repository. You get a visual project graph, nx affected commands that only rebuild what changed, and optional remote caching that cuts CI times dramatically. This post walks through the full setup from scratch.

If your organization runs more than one Angular application, you have almost certainly hit the same wall. The dashboard app has a <shared-button> component. The marketing site also needs a <shared-button> component. So someone copies the files, tweaks them slightly, and now there are two diverging implementations. Six months later a bug fix in one never lands in the other.

The solution is a monorepo — a single Git repository that houses all your apps and all your shared libraries. Nx is the toolchain that makes Angular monorepos practical at scale, and in 2026 it has become the default choice for enterprise Angular teams. This guide covers Nx monorepo Angular libraries end to end: workspace setup, creating and consuming libraries, the project graph, affected builds, and remote caching with Nx Cloud.


The Problem: Duplicated Code Across Angular Apps

Before reaching for Nx, it helps to be concrete about what breaks without it.

A typical multi-app setup without a monorepo looks like this: three separate repositories, each with its own package.json, each installing the same version of @angular/core (or worse — slightly different versions). When you add a fourth app, you copy-paste the authentication service from app one. When a security fix lands, you remember to update two of the four apps but miss the third.

The technical debt compounds quickly:

  • No single source of truth for shared types, interfaces, or API contracts.
  • Dependency version drift — one app is on Angular 22, another is stuck on Angular 21.
  • No easy way to know whether changing a shared utility will break a downstream app.

An Nx monorepo Angular libraries setup solves all three. Libraries live in a libs/ folder. Apps import from them via TypeScript path aliases. Nx tracks the dependency graph and tells you exactly which apps are affected by any library change.


Setting Up an Nx Workspace for Angular Libraries

Create the workspace

npx create-nx-workspace@latest my-org --preset=angular-monorepo --appName=dashboard --style=scss
cd my-org

Nx scaffolds a workspace with one Angular app inside apps/dashboard/. The key files at the root are:

  • nx.json — Nx configuration: default runner, task pipeline, caching rules.
  • tsconfig.base.json — TypeScript path aliases for all libraries.
  • project.json inside each app/library — replaces angular.json entries.

Generate a second app

nx generate @nx/angular:app marketing-site --style=scss --standalone

You now have apps/dashboard/ and apps/marketing-site/. Both apps have their own project.json, but they share the root tsconfig.base.json — which is how the path aliases for shared libraries will work.


Creating a Shared Angular Library in Nx

This is the core concept in an Nx monorepo Angular libraries workflow. A library is just a TypeScript package that lives inside your workspace. It has its own project.json, its own src/index.ts barrel, and a path alias registered in tsconfig.base.json.

Generate a UI component library

nx generate @nx/angular:library ui --directory=libs/ui --standalone --buildable

Nx creates libs/ui/ with:

libs/ui/
  src/
    lib/
      ui.component.ts      ← placeholder component
    index.ts               ← public barrel exports
  project.json
  tsconfig.json
  tsconfig.lib.json

And in tsconfig.base.json it registers the path alias automatically:

{
  "compilerOptions": {
    "paths": {
      "@my-org/ui": ["libs/ui/src/index.ts"]
    }
  }
}

Add a real component

nx generate @nx/angular:component button --project=ui --standalone --export

This generates libs/ui/src/lib/button/button.component.ts and adds it to the barrel libs/ui/src/index.ts. Here is what the component looks like in modern Angular:

// libs/ui/src/lib/button/button.component.ts
import { Component, input, output, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';

export type ButtonVariant = 'primary' | 'secondary' | 'ghost';

@Component({
  selector: 'my-org-button',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <button
      [class]="'btn btn--' + variant()"
      [disabled]="disabled()"
      (click)="clicked.emit()"
    >
      <ng-content />
    </button>
  `,
  styleUrl: './button.component.scss',
})
export class ButtonComponent {
  variant = input<ButtonVariant>('primary');
  disabled = input(false);
  clicked = output<void>();
}

Consume the library from an app

Inside apps/dashboard/src/app/app.component.ts:

import { Component } from '@angular/core';
import { ButtonComponent } from '@my-org/ui'; // resolved via tsconfig path alias

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ButtonComponent],
  template: `
    <my-org-button variant="primary" (clicked)="save()">
      Save
    </my-org-button>
  `,
})
export class AppComponent {
  save() {
    console.log('saved');
  }
}

No symlinks, no npm link, no local package publishing — the path alias in tsconfig.base.json handles resolution at build time for both nx serve and nx build.


The Nx Project Graph: Understanding Shared Angular Library Dependencies

Run this in your workspace root:

nx graph

Nx opens a browser UI showing every app and library as a node and every import as an edge. If apps/dashboard imports @my-org/ui, there is a directed edge from dashboardui. If apps/marketing-site also imports @my-org/ui, both apps depend on it.

This graph is not just for visualization — it powers nx affected.

Affected commands

The most valuable feature in an Nx monorepo for Angular library development is the ability to run tasks only on what changed:

# Only test and build apps/libs touched by your current branch
nx affected -t test
nx affected -t build
nx affected -t lint

Nx compares your branch against main (or any base ref), identifies which files changed, and walks the project graph to find every app and library that transitively depends on those files. Only those projects run.

In a 20-app monorepo where you changed only libs/ui/button, nx affected -t test might run tests for 3 apps instead of 20. On large teams this reduces CI from 40 minutes to 8 minutes or less.


Organizing Libraries by Type

The Nx documentation recommends organizing shared Angular libraries into four categories. This prevents the "libs/everything-goes-here" anti-pattern:

libs/
  ui/           ← Presentational components with no business logic
  data-access/  ← Services, HTTP calls, state stores
  util/         ← Pure functions, pipes, validators
  feature/      ← Smart components that compose ui + data-access

Generate each as its own library:

nx generate @nx/angular:library data-access --directory=libs/data-access --standalone --buildable
nx generate @nx/angular:library util --directory=libs/util --standalone

Enforce that lower-level libraries cannot depend on higher-level ones using Nx module boundary rules in eslint.config.js. This catches illegal imports at lint time rather than at runtime.


Nx Cloud Remote Caching

Nx has a local computation cache. If you run nx build dashboard and nothing changed, the second run is instant — it restores outputs from ~/.cache/nx. Nx Cloud extends that cache across your whole team and your CI servers.

nx connect

After connecting, every nx build, nx test, and nx lint result is stored remotely. When your CI pipeline picks up a pull request, it checks the remote cache first. If a colleague already built the same commit, your CI restores the output rather than recompiling.

For a 10-person Angular team, Nx Cloud typically reduces total CI compute by 40–70%. The free tier covers small teams — see nx.dev/nx-cloud for details.


Using ZyraUI for Shared Component Libraries

One of the most common reasons teams reach for an Nx monorepo Angular setup is to build an internal design system — the same reason ZyraUI exists as a standalone library. Whether you are building your own design system inside Nx or consuming an existing one, the pattern is the same: install once at the workspace root, import in each app that needs it.

Here is how you would set up a ZyraUI-powered card component inside your libs/ui library:

// libs/ui/src/lib/article-card/article-card.component.ts
import { Component, input, ChangeDetectionStrategy } from '@angular/core';
import { ZyraCardComponent, ZyraBadgeComponent } from '@zyra-ui/angular';

@Component({
  selector: 'my-org-article-card',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [ZyraCardComponent, ZyraBadgeComponent],
  template: `
    <zyra-card>
      <zyra-badge [label]="category()" variant="info" />
      <h3>{{ title() }}</h3>
      <p>{{ excerpt() }}</p>
    </zyra-card>
  `,
})
export class ArticleCardComponent {
  title = input.required<string>();
  excerpt = input.required<string>();
  category = input('General');
}

Because ZyraUI components are standalone, they drop straight into the imports array of your library component — no module wrappers required. Both apps/dashboard and apps/marketing-site consume ArticleCardComponent via @my-org/ui and transitively get ZyraUI's styling through the single workspace-root install.

You can explore all 60+ free components at zyraui.dev — including cards, modals, tables, forms, and data display components at ZyraUI components. If you want to customize the design system tokens for your brand, check out the ZyraUI theming page.


Wrapping up

An Nx monorepo with Angular libraries solves a real problem that every growing frontend team eventually hits: duplicated code, version drift, and no visibility into what depends on what. The setup is fast — create-nx-workspace gets you running in minutes — and the payoff compounds as the project grows. Affected commands and remote caching mean your CI stays fast even as the codebase scales.

If you are building your own shared component library inside Nx, pair it with a solid foundation like ZyraUI so you can focus on product-specific components rather than re-implementing buttons and modals. Check the ZyraUI docs for the full API reference and installation guide.


Frequently asked questions

What is an Nx monorepo for Angular libraries, and why should I use one?

An Nx monorepo is a single Git repository containing multiple Angular apps and shared libraries managed by the Nx toolchain. You should use one when you have more than one app that shares components, services, or utilities — it eliminates code duplication, gives you a visual dependency graph, and dramatically speeds up CI via affected commands and remote caching. Teams with even two Angular apps typically see immediate benefits.

How does nx affected know which Angular libraries changed?

Nx statically analyzes TypeScript imports across your entire workspace to build a project dependency graph. When you run nx affected, it diffs your current branch against the base branch (usually main), finds which source files changed, and traverses the dependency graph to identify every project that transitively imports those files. Only those projects are included in the affected set — everything else is skipped.

Can I publish Nx Angular libraries to npm?

Yes. Libraries generated with --buildable produce a production-ready dist folder with a proper package.json, type declarations, and FESM/CJS outputs. Libraries generated with --publishable additionally support nx release, which handles version bumping, changelog generation, and npm publish in a coordinated workflow. For internal use within the workspace, publishing to npm is not required — the TypeScript path aliases handle resolution directly.

Does Nx work with the latest Angular standalone component APIs?

Yes, Nx has first-class support for Angular standalone components. The @nx/angular generators accept a --standalone flag and produce components, libraries, and apps using standalone: true, inject(), signal inputs and outputs, and the @if/@for control flow syntax. You can also use Nx with Angular 22's signal-based forms, resource(), and httpResource() — Nx itself is framework-version-agnostic at the workspace level.

Topics
AngularNxMonorepoComponent LibraryFrontend Architecture