At first glance, it looks like a massive, deeply nested wall of text. It’s easy to ignore it and let the Angular CLI do its magic behind the scenes. However, understanding angular.json is a rite of passage for Angular developers. It is the heart of your workspace, dictating how your app is built, served, tested, and deployed.
Let’s break down the angular.json file, translating its key-value pairs from machine-speak into plain English.
The Root Level: The Lay of the Land
When you collapse all the nested objects, the root of angular.json is surprisingly simple. It defines the workspace environment.
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": { ... }
}
$schema: This tells your code editor where to find the rules for this JSON file. It’s what gives you autocomplete and validation when you are editingangular.jsonin editors like VS Code.version: The configuration file version (currently1).newProjectRoot: If you generate additional applications or libraries within this workspace usingng generate applicationorng generate library, this is the folder where they will be placed.projects: This is where the magic happens. It contains the configuration details for every app and library in your workspace.
The projects Object: Your Apps and Libraries
Inside projects, you will find a key for your main application (usually named whatever you called your app when running ng new). If your app is named my-awesome-app, you’ll see:
"projects": {
"my-awesome-app": {
"projectType": "application",
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": { ... }
}
}
projectType: Tells the CLI if this specific project is an"application"(runs in a browser) or a"library"(a collection of reusable code for other apps).root: The root directory for this specific project. For the default, primary app, this is usually empty ("") because it sits at the root of the workspace.sourceRoot: The folder where your actual source code (TypeScript, HTML, CSS) lives. By default, this is"src".prefix: The default prefix used when generating new components. If it’s"app", your components will look like<app-header></app-header>.architect: The workhorse of the file. It defines the automated tasks (targets) for your project.
Deep Dive into architect: Building, Serving, and Testing
The architect node contains commands (also called “targets”) like build, serve, test, and extract-i18n. When you run a command like ng serve in your terminal, the Angular CLI looks inside the architect.serve block to figure out exactly what to do.
Let’s look at the most important target: build.
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/my-awesome-app",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": []
},
"configurations": { ... }
}
}
1. builder
This is the tool under the hood that executes the task. For standard web apps, it’s usually @angular-devkit/build-angular:browser (using Webpack) or :application (if you’re using the newer esbuild system).
2. options
These are the default settings used when you run ng build.
outputPath: Where the compiled, production-ready files will be saved (usually adist/folder).index: The main HTML file that acts as the entry point.main: The main TypeScript file that bootstraps your Angular application.assets: An array of files or folders (like images, fonts, or the favicon) that should be copied directly into your output folder without being modified.styles: Global stylesheets. If you are using a CSS framework like Bootstrap, you often add the path to its CSS file here.scripts: Global JavaScript files. Similar to styles, you can inject external JS libraries here.
3. configurations
This is arguably the most powerful feature in the file. It allows you to override the default options based on specific environments (like production or development).
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"sourceMap": false
},
"development": {
"optimization": false,
"sourceMap": true
}
}
When you run ng build --configuration production, Angular merges the options block with the configurations.production block.
fileReplacements: This is incredibly useful. It swaps out files during the build process. Usually, it’s used to swap your localenvironment.ts(pointing to a local API) withenvironment.prod.ts(pointing to your live, production API).optimization/sourceMap: You can instruct the builder to minify code for production but keep source maps active for debugging during development.
The serve Target
Finally, let’s briefly look at the serve target. This is what runs when you type ng serve.
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "my-awesome-app:build"
},
"configurations": {
"production": {
"browserTarget": "my-awesome-app:build:production"
}
}
}
Notice the browserTarget key. The serve command doesn’t actually compile your code itself; it just tells the build target to run in memory and host the result on localhost:4200.
Summary
While angular.json can feel overwhelming, it’s highly logical once you understand the hierarchy:
- Workspace (
projects) -> - Specific App (
my-awesome-app) -> - Command (
architect->build) -> - How to do it (
builder,options,configurations).
Mastering this file allows you to effortlessly add global stylesheets, manage multiple environments, and control exactly how your Angular app gets bundled for the real world.