Skip to main content

Advanced Angular CLI & Workspace Management

Angular CLI provides a comprehensive development experience from project creation to code generation, library management, and build optimization. Leveraging the CLI in large-scale development dramatically improves productivity and code consistency.

1. Unified Code Generation with ng generate

ng generate (short: ng g) automatically generates files like components and services following Angular best practices. When every team member uses the same command, file naming, directory structure, and test file conventions are automatically standardized.

Key generate commands

Generating components
# Standalone component (Angular 17+ default)
ng g component features/user-profile

# NgModule-based component (legacy)
ng g component features/user-profile --no-standalone

# Compact with inline styles and inline template
ng g component shared/badge --inline-style --inline-template

# Skip test files
ng g component shared/spinner --skip-tests
Generating services, guards, and more
# Service
ng g service core/auth

# Directive
ng g directive shared/directives/highlight

# Pipe
ng g pipe shared/pipes/truncate

# Guard (route protection)
ng g guard core/guards/auth --implements CanActivate

# HTTP Interceptor
ng g interceptor core/interceptors/auth-token

# Resolver (pre-fetch route data)
ng g resolver features/user/user-detail

Key options reference

OptionDescriptionDefault
--standaloneGenerate as a standalone componenttrue (v17+)
--skip-testsSkip .spec.ts file generationfalse
--inline-styleWrite styles inside .ts filefalse
--inline-templateWrite template inside .ts filefalse
--flatDon't create a subdirectoryfalse
--dry-runPreview without actually writingfalse
--prefixSpecify selector prefixapp
Comparison with React/Next.js

React has no official code generation tool — components are created manually. Teams sometimes adopt plop.js or hygen, but there's no standardized tool like Angular CLI.

# React (manual or third-party)
touch src/components/UserProfile/index.tsx
touch src/components/UserProfile/UserProfile.test.tsx

# Angular (standard)
ng g component features/user-profile
# → user-profile.component.ts, .html, .css, .spec.ts auto-generated

Generated file example

ng g component features/user-profile --standalone
# CREATE src/app/features/user-profile/user-profile.component.ts
# CREATE src/app/features/user-profile/user-profile.component.html
# CREATE src/app/features/user-profile/user-profile.component.css
# CREATE src/app/features/user-profile/user-profile.component.spec.ts
src/app/features/user-profile/user-profile.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
selector: 'app-user-profile',
standalone: true,
imports: [CommonModule],
templateUrl: './user-profile.component.html',
styleUrls: ['./user-profile.component.css']
})
export class UserProfileComponent {}

2. Creating and Managing Angular Libraries

The Angular CLI includes built-in support for creating and managing shared libraries within a monorepo. Extracting UI component libraries, utility functions, and shared services into libraries makes reuse across multiple apps straightforward.

Creating a library

Library generation command
ng generate library mylib

# Workspace structure:
# projects/
# mylib/
# src/
# lib/
# mylib.component.ts
# mylib.module.ts
# mylib.service.ts
# public-api.ts ← export entry point
# ng-package.json ← ng-packagr config
# tsconfig.lib.json
# tsconfig.spec.json

Managing exports with public-api.ts

projects/mylib/src/public-api.ts
// Centrally manage the library's public API here
export * from './lib/mylib.component';
export * from './lib/mylib.service';
export * from './lib/button/button.component';
export * from './lib/models/user.model';
Why public-api.ts matters

public-api.ts is the library's "front door". Anything not exported here cannot be used outside the library — a great encapsulation mechanism.

Building the library

Library build using ng-packagr
ng build mylib

# Output in dist/mylib/:
# dist/mylib/
# esm2022/
# fesm2022/
# package.json
# index.d.ts

Consuming the library within the workspace

tsconfig.json (auto-configured)
{
"compilerOptions": {
"paths": {
"mylib": ["dist/mylib"]
}
}
}
Using the library in an app
import { MylibComponent } from 'mylib';

@Component({
standalone: true,
imports: [MylibComponent],
template: `<mylib-root />`
})
export class AppComponent {}

Watch mode during development

# Watch-build the library while running the app
ng build mylib --watch &
ng serve
Comparison with Turborepo
FeatureAngular LibraryReact + Turborepo
Library creationng g library mylibturbo gen workspace --name mylib
Build toolng-packagr (Angular-optimized)tsc / tsup / vite
Path aliasesAuto-configured via tsconfigManual tsconfig setup
Bundle formatsESM + CJS + UMDDepends on config
Type definitionsAuto-generatedAuto-generated

Angular CLI automatically applies Angular-specific optimizations (partial Ivy compilation) during library builds.


3. Schematics (Automated Code Generation)

Schematics is the code generation and transformation engine behind Angular CLI. It uses AST-based source analysis and transformation, powering ng generate, ng add, and ng update under the hood.

How Schematics work

Running third-party Schematics

Running Schematics via ng add
# Add Angular Material to workspace
# → auto-updates package.json, adds module imports, configures themes
ng add @angular/material

# Add ESLint
ng add @angular-eslint/schematics

# Add PWA support
ng add @angular/pwa

Creating a custom Schematic

Creating a Schematic project
npm install -g @angular-devkit/schematics-cli
schematics blank --name=my-schematics
cd my-schematics
src/my-schematics/index.ts (simple example)
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';

export function myComponent(options: { name: string }): Rule {
return (tree: Tree, context: SchematicContext) => {
const content = `
import { Component } from '@angular/core';

@Component({
selector: 'app-${options.name}',
standalone: true,
template: '<div>${options.name} works!</div>'
})
export class ${capitalize(options.name)}Component {}
`.trim();

tree.create(`src/app/${options.name}/${options.name}.component.ts`, content);
return tree;
};
}

function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}

Migration Schematics (used by ng update)

ng update automatically runs migration Schematics when upgrading packages.

ng update example output
ng update @angular/core @angular/cli

# Output:
# Using package manager: npm
# Collecting installed dependencies...
# Fetching dependency metadata from registry...
# Package '@angular/core' is already up-to-date.
# Updating package.json with dependency @angular/core @ "17.0.0"...
# Running migrations of package '@angular/core'...
# ✓ Migration: Update standalone API usage.
# ✓ Migration: Remove deprecated lifecycle hooks.
# UPDATE src/app/app.module.ts (345 bytes)
# UPDATE src/app/main.ts (214 bytes)
Comparison with React ecosystem
ToolPurposeMechanism
Angular SchematicsCode generation & migrationAST transform, official standard
Plop.js (React)Template-based code generationHandlebars templates
Hygen (React)File generationEJS templates
codemod (React)Migrationjscodeshift AST transform

Angular's ng update ships with official migration Schematics, making major version upgrades safe and automated. React major upgrades often require manual code changes as official automation tooling is limited.


4. Monorepo Management with Nx

Nx is a build system for large-scale Angular monorepos. It manages multiple apps and libraries in a single repository, using dependency graph-based incremental builds and tests to dramatically speed up development.

Creating an Nx workspace

Initializing an Nx workspace
# Create new workspace
npx create-nx-workspace@latest my-org --preset=angular-monorepo

# Add Nx to an existing Angular workspace
ng add @nx/angular

Monorepo directory structure

my-org/
├── apps/
│ ├── app-a/ ← Angular App A
│ └── app-b/ ← Angular App B
├── libs/
│ ├── shared/ ← Shared library
│ │ ├── ui/ ← Shared UI components
│ │ └── util/ ← Shared utilities
│ └── feature/ ← Feature library
├── nx.json ← Nx configuration
└── package.json

Project dependency graph

Visualize the dependency graph interactively:

nx graph
# → Opens dependency graph in browser

affected commands (incremental builds and tests)

Build and test only changed projects
# Build only projects affected by changes vs main branch
nx affected:build --base=main

# Test only affected projects
nx affected:test --base=main

# Lint only affected projects
nx affected:lint --base=main

# Common CI usage
nx affected --target=build --base=origin/main --head=HEAD

Cache and remote cache

Leveraging the cache
# Local cache is automatically saved to ~/.nx/cache
nx build app-a
# → Second run completes instantly from cache

# Remote cache with Nx Cloud (shared across team)
nx connect
nx.json (cache configuration)
{
"tasksRunnerOptions": {
"default": {
"runner": "@nx/cloud",
"options": {
"cacheableOperations": ["build", "test", "lint", "e2e"],
"accessToken": "YOUR_NX_CLOUD_TOKEN"
}
}
}
}

Module boundary rules

.eslintrc.json (boundary rule configuration)
{
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{
"sourceTag": "scope:app",
"onlyDependOnLibsWithTags": ["scope:shared", "scope:feature"]
},
{
"sourceTag": "scope:feature",
"onlyDependOnLibsWithTags": ["scope:shared"]
},
{
"sourceTag": "scope:shared",
"onlyDependOnLibsWithTags": ["scope:shared"]
}
]
}
]
}
}
Comparison with Next.js + Turborepo
FeatureNx (Angular)Turborepo (Next.js)
Incremental buildsnx affected:buildturbo run build --filter=[origin/main]
CachingLocal + Nx CloudLocal + Vercel Remote Cache
Dependency graphnx graph (GUI)turbo run --graph
Boundary rules@nx/enforce-module-boundariesCustom lint rules
Code generationnx generateturbo gen
Framework focusAngular-optimizedFramework-agnostic

Nx excels at enterprise features: task orchestration, CI integration, and code ownership management.


5. angular.json Workspace Configuration

angular.json is the central configuration file for the entire Angular CLI workspace. It manages build, serve, and test settings, bundle size limits, and environment switching in one place.

angular.json structure overview

angular.json (key sections)
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"my-app": {
"projectType": "application",
"root": "",
"sourceRoot": "src",
"architect": {
"build": { },
"serve": { },
"test": { },
"lint": { }
}
}
}
}

Detailed build target configuration

angular.json build configuration
{
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/my-app",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets",
{ "glob": "**/*", "input": "public", "output": "/" }
],
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
}
}
}

budgets (bundle size limits)

budgets configuration example
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
},
{
"type": "anyScript",
"maximumWarning": "150kB",
"maximumError": "300kB"
}
]
typeTarget
initialTotal initial load bundle
anyScriptIndividual script files
anyComponentStyleIndividual component CSS
bundleA specific named bundle

fileReplacements (environment-specific file swapping)

src/environments/environment.ts (development)
export const environment = {
production: false,
apiUrl: 'http://localhost:3000/api',
featureFlags: { newDashboard: true }
};
src/environments/environment.prod.ts (production)
export const environment = {
production: true,
apiUrl: 'https://api.example.com',
featureFlags: { newDashboard: false }
};
Building with a specific configuration
ng build --configuration=production
ng serve --configuration=development
Comparison with Next.js

Next.js uses .env.local, .env.production etc. for environment switching. Angular's fileReplacements enables type-safe environment configuration with full TypeScript type checking — an advantage over plain string-based env vars.


6. Custom Builders

Builders (formerly Architects) let you replace the implementation behind Angular CLI commands (ng build, ng serve, ng test, etc.). The @angular-devkit/architect package provides the foundation.

Switching to the esbuild builder (Angular 17+)

Angular 17+ changed the default builder to application (esbuild-based). Existing projects can migrate from the browser (Webpack) builder.

angular.json (esbuild builder)
{
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"browser": "src/main.ts"
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"buildTarget": "my-app:build"
}
}
}
}

Build time comparison

BuilderInitial buildRebuild (incremental)Bundle size
Webpack (old browser)~30–60s~5–10sBaseline
esbuild (new application)~5–10s~1–3sEqual or smaller

Referencing a custom builder

When a completely custom build process is needed, you can create and reference a local builder.

angular.json (custom builder reference)
{
"architect": {
"build": {
"builder": "./tools/builders/custom-builder:build"
}
}
}
tools/builders/custom-builder/index.ts
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';

interface Options extends JsonObject {
outputPath: string;
}

async function customBuild(options: Options, context: BuilderContext): Promise<BuilderOutput> {
context.logger.info(`Building to ${options.outputPath}...`);
// Custom build logic
return { success: true };
}

export default createBuilder(customBuild);

Notable third-party builders

Commonly used builders
# Use Jest as test runner
ng add @briebug/jest-schematic

# Storybook integration
ng add @storybook/angular

# Add Tailwind CSS
ng add ngx-tailwind
Comparison with Vite

Compared to Next.js / Vite-based projects, Angular 17+'s esbuild builder achieves equivalent or faster build speeds. HMR (Hot Module Replacement) has also been significantly improved.


7. Quality Automation via CLI

Angular CLI includes commands to automate code quality, dependency management, and deployment.

ng lint (ESLint integration)

ESLint setup and execution
# Add ESLint
ng add @angular-eslint/schematics

# Run lint
ng lint

# Auto-fix issues
ng lint --fix
.eslintrc.json (Angular ESLint configuration)
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nx"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@angular-eslint/recommended"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{ "type": "attribute", "prefix": "app", "style": "camelCase" }
],
"@angular-eslint/component-selector": [
"error",
{ "type": "element", "prefix": "app", "style": "kebab-case" }
]
}
}
]
}

ng update (automated dependency updates)

ng update usage
# Check for available updates
ng update

# Update Angular core packages (runs migration Schematics automatically)
ng update @angular/core @angular/cli

# Update to a specific version
ng update @angular/core@17

# Example output:
# ✓ Migration: Migrate to standalone APIs.
# ✓ Migration: Replace deprecated providedIn 'any'.
# UPDATE src/app/app.module.ts
# UPDATE src/app/main.ts
Comparison with React

React and Next.js major upgrades often require manual intervention — you need to research breaking changes and update code yourself. Angular's ng update runs official migration Schematics automatically, significantly reducing the cost of large-scale refactoring during version upgrades.

ng deploy (deployment automation)

Deploying to Firebase
# Add Firebase Hosting deploy builder
ng add @angular/fire

# Run deploy
ng deploy

# Deploy a specific project
ng deploy --project=my-app
Deploying to Azure Static Web Apps
ng add @azure/ng-deploy
ng deploy

GitHub Actions CI/CD pipeline

.github/workflows/ci.yml
name: Angular CI

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint
run: ng lint

- name: Test
run: ng test --watch=false --browsers=ChromeHeadless --code-coverage

- name: Build (production)
run: ng build --configuration production

- name: Upload coverage
uses: codecov/codecov-action@v4
with:
directory: ./coverage

deploy:
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build for production
run: ng build --configuration production

- name: Deploy to Firebase
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
channelId: live

Quality automation commands summary

CommandPurposeReact/Next.js equivalent
ng lintRun ESLinteslint .
ng testKarma/Jest testsjest
ng e2eE2E tests (Cypress/Playwright)cypress run
ng updateUpdate dependencies + run migrationsnpm-check-updates + manual
ng deployDeploy to hostingvercel / firebase deploy
ng build --stats-jsonGenerate bundle analysis JSONANALYZE=true next build
Bundle size analysis
ng build --stats-json
npx webpack-bundle-analyzer dist/my-app/stats.json
Summary

Angular CLI is a powerful, unified toolchain covering everything from code generation to quality management and deployment. Work that requires assembling multiple third-party tools in React/Next.js (ESLint, Prettier, Plop, Turborepo, Vercel CLI, etc.) is handled consistently with a single ng command in Angular. In large team environments, this uniformity significantly contributes to enforcing standards, simplifying onboarding, and reducing CI/CD setup costs.