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
# 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
# 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
| Option | Description | Default |
|---|---|---|
--standalone | Generate as a standalone component | true (v17+) |
--skip-tests | Skip .spec.ts file generation | false |
--inline-style | Write styles inside .ts file | false |
--inline-template | Write template inside .ts file | false |
--flat | Don't create a subdirectory | false |
--dry-run | Preview without actually writing | false |
--prefix | Specify selector prefix | app |
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
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
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
// 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';
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
ng build mylib
# Output in dist/mylib/:
# dist/mylib/
# esm2022/
# fesm2022/
# package.json
# index.d.ts
Consuming the library within the workspace
{
"compilerOptions": {
"paths": {
"mylib": ["dist/mylib"]
}
}
}
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
| Feature | Angular Library | React + Turborepo |
|---|---|---|
| Library creation | ng g library mylib | turbo gen workspace --name mylib |
| Build tool | ng-packagr (Angular-optimized) | tsc / tsup / vite |
| Path aliases | Auto-configured via tsconfig | Manual tsconfig setup |
| Bundle formats | ESM + CJS + UMD | Depends on config |
| Type definitions | Auto-generated | Auto-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
# 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
npm install -g @angular-devkit/schematics-cli
schematics blank --name=my-schematics
cd my-schematics
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 @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)
| Tool | Purpose | Mechanism |
|---|---|---|
| Angular Schematics | Code generation & migration | AST transform, official standard |
| Plop.js (React) | Template-based code generation | Handlebars templates |
| Hygen (React) | File generation | EJS templates |
| codemod (React) | Migration | jscodeshift 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
# 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 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
# 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
{
"tasksRunnerOptions": {
"default": {
"runner": "@nx/cloud",
"options": {
"cacheableOperations": ["build", "test", "lint", "e2e"],
"accessToken": "YOUR_NX_CLOUD_TOKEN"
}
}
}
}
Module boundary rules
{
"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"]
}
]
}
]
}
}
| Feature | Nx (Angular) | Turborepo (Next.js) |
|---|---|---|
| Incremental builds | nx affected:build | turbo run build --filter=[origin/main] |
| Caching | Local + Nx Cloud | Local + Vercel Remote Cache |
| Dependency graph | nx graph (GUI) | turbo run --graph |
| Boundary rules | @nx/enforce-module-boundaries | Custom lint rules |
| Code generation | nx generate | turbo gen |
| Framework focus | Angular-optimized | Framework-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
{
"$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
{
"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": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
},
{
"type": "anyScript",
"maximumWarning": "150kB",
"maximumError": "300kB"
}
]
type | Target |
|---|---|
initial | Total initial load bundle |
anyScript | Individual script files |
anyComponentStyle | Individual component CSS |
bundle | A specific named bundle |
fileReplacements (environment-specific file swapping)
export const environment = {
production: false,
apiUrl: 'http://localhost:3000/api',
featureFlags: { newDashboard: true }
};
export const environment = {
production: true,
apiUrl: 'https://api.example.com',
featureFlags: { newDashboard: false }
};
ng build --configuration=production
ng serve --configuration=development
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.
{
"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
| Builder | Initial build | Rebuild (incremental) | Bundle size |
|---|---|---|---|
Webpack (old browser) | ~30–60s | ~5–10s | Baseline |
esbuild (new application) | ~5–10s | ~1–3s | Equal or smaller |
Referencing a custom builder
When a completely custom build process is needed, you can create and reference a local builder.
{
"architect": {
"build": {
"builder": "./tools/builders/custom-builder:build"
}
}
}
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
# Use Jest as test runner
ng add @briebug/jest-schematic
# Storybook integration
ng add @storybook/angular
# Add Tailwind CSS
ng add ngx-tailwind
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)
# Add ESLint
ng add @angular-eslint/schematics
# Run lint
ng lint
# Auto-fix issues
ng lint --fix
{
"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)
# 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
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)
# Add Firebase Hosting deploy builder
ng add @angular/fire
# Run deploy
ng deploy
# Deploy a specific project
ng deploy --project=my-app
ng add @azure/ng-deploy
ng deploy
GitHub Actions CI/CD pipeline
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
| Command | Purpose | React/Next.js equivalent |
|---|---|---|
ng lint | Run ESLint | eslint . |
ng test | Karma/Jest tests | jest |
ng e2e | E2E tests (Cypress/Playwright) | cypress run |
ng update | Update dependencies + run migrations | npm-check-updates + manual |
ng deploy | Deploy to hosting | vercel / firebase deploy |
ng build --stats-json | Generate bundle analysis JSON | ANALYZE=true next build |
ng build --stats-json
npx webpack-bundle-analyzer dist/my-app/stats.json
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.