Angular 110 🅰️ Build Optimizations and Budgets
An Angular application is not the same in development as it is in production. The development build is large, readable, and optimized for fast rebuilds. The production build is small, minified, and optimized for fast delivery to the user. The difference between the two is the build configuration. The Angular CLI applies a set of optimizations in production mode that reduce the bundle size by 50–80% compared to the development build .
But optimization is not enough on its own. An application that grows without limits will eventually become slow to load, regardless of the optimizations. Budgets are the guardrails. They set size thresholds that the build checks on every run. If the bundle exceeds the threshold, the build warns or fails. Budgets are how the team enforces a performance contract on the application .
The previous chapters covered performance, testing, and debugging. This chapter covers the build itself: what the production build does, how to configure it, and how to use budgets to prevent the bundle from growing out of control.
Key point: The ng build --configuration production command enables a set of optimizations that are disabled in the development build. These include Ahead-of-Time (AOT) compilation, minification, tree-shaking, dead-code elimination, and output hashing . The budgets array in angular.json defines size thresholds for the initial bundle and the component styles. When the bundle exceeds a threshold, the CLI reports a warning or an error. The budget is the mechanism that makes bundle size a build-time concern rather than a production surprise.
Why Build Optimizations Matter
The development build is not meant to be deployed. It is meant to be run on a local machine, where the developer values fast rebuilds over small bundles. The production build is meant to be delivered to the user, where every kilobyte counts.
The bundle size problem. A development build of a medium-sized Angular application can easily exceed several megabytes. The same application in a production build is often under a megabyte. The difference is the optimizations. Without them, the application is slow to download, slow to parse, and slow to execute .
The compiler problem. The development build includes the Angular compiler in the bundle. The compiler is large. The production build uses Ahead-of-Time (AOT) compilation, which compiles the templates at build time and removes the compiler from the bundle. The bundle is smaller and the rendering is faster .
The dead code problem. An application imports libraries that contain more code than the application uses. The development build includes the entire library. The production build uses tree-shaking to remove the unused exports. The result is a bundle that contains only the code the application actually executes .
The debugging problem. The development build includes source maps. Source maps are large. The production build disables them by default. The bundle is smaller. If source maps are needed for error tracking, they can be enabled with the hidden option, which generates the maps but does not reference them in the bundle .
The trade-off. The production build is slower to produce. AOT compilation takes longer than Just-in-Time (JIT) compilation. Minification takes time. The trade-off is acceptable: the build runs on the developer’s machine or in the CI pipeline, and the user gets a faster application.
a. The Production Build Optimizations
The ng build --configuration production command applies a set of optimizations. The --configuration production flag selects the production configuration, which is defined in angular.json .
Ahead-of-Time (AOT) Compilation. The templates are compiled at build time. The Angular compiler is removed from the bundle. The template errors are caught at build time, not at runtime .
Minification. The JavaScript and CSS are minified. The whitespace is removed. The variable names are shortened. The result is a smaller file that the browser can parse faster .
Tree-Shaking. The unused code is removed. The bundler starts from the entry point, crawls the imports, and removes the exports that are not reachable. The result is a bundle that contains only the code the application uses .
Dead Code Elimination. The code that is unreachable or has no effect is removed. The result is a smaller bundle and a faster execution .
Output Hashing. The output file names include a hash of the content. When the content changes, the hash changes. The browser downloads the new file. When the content is unchanged, the hash is unchanged. The browser uses the cached version. This is the mechanism that makes caching safe .
Source Map Configuration. Source maps are disabled by default. If they are needed, they can be enabled with the hidden option. The hidden option generates the maps but does not reference them in the bundle. The maps are available to error-tracking tools but not to the browser’s DevTools .
The optimization flag controls the optimizations. The flag accepts a boolean or an object. The object form allows the minification and the tree-shaking to be enabled separately. The buildOptimizer flag enables the build optimizer, which removes Angular decorators and other unnecessary code .
b. Budget Configuration
The budgets array in angular.json defines the size thresholds. Each budget entry has a type, a maximumWarning, and a maximumError. The build reports a warning when the bundle exceeds the warning threshold and an error when the bundle exceeds the error threshold .
The budget types are :
| Type | Purpose |
|---|---|
bundle | The size of a specific bundle |
initial | The size of the JavaScript and CSS needed for bootstrapping |
allScript | The size of all scripts |
all | The size of the entire application |
anyComponentStyle | The size of any one component stylesheet |
anyScript | The size of any one script |
any | The size of any file |
The size values can be specified in bytes (123 or 123b), kilobytes (123kb), megabytes (123mb), or as a percentage (12%) relative to a baseline .
A typical budget configuration:
{
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
]
}
The initial budget covers the JavaScript and CSS that the browser must download before the application boots. The anyComponentStyle budget covers the size of any single component’s stylesheet. The defaults are 500 kB warning and 1 MB error for the initial bundle, and 2 kB warning and 4 kB error for the component styles .
When the build exceeds the warning threshold, the CLI prints a warning but the build succeeds. When the build exceeds the error threshold, the build fails. The error prevents the deployment of an application that has grown beyond the acceptable size .
The budget is the mechanism that makes bundle size a build-time concern. Without it, the bundle grows silently until the application is slow. With it, the team is notified on the build that introduces the growth. The notification is the opportunity to investigate and to fix the cause .
c. Build Analysis and Optimization
The budget tells the team that the bundle is too large. The build analysis tells the team why. The --stats-json flag produces a statistics file that describes every module in the bundle. The file is consumed by the bundle analyzer .
ng build --configuration production --stats-json
npx webpack-bundle-analyzer dist/my-app/stats.json
The analyzer renders the bundle as a treemap. The large blocks are the optimization opportunities. A large library that is not tree-shaken can be replaced. A module that is not needed on the initial route can be lazy-loaded. A component style that exceeds the budget can be split or simplified .
The isolatedModules option is a build optimization introduced in Angular 18.2. When enabled in tsconfig.json, TypeScript code is transpiled by the bundler instead of by TypeScript. The bundler can inline enums and remove the Babel-based optimization passes. The result is a faster production build, up to 10% faster .
{
"compilerOptions": {
"isolatedModules": true
}
}
The isolatedModules option requires that useDefineForClassFields be either removed or set to true. The combination ensures that the output code is optimized and the build is fast .
The experimental chunk optimizer, available in Angular 18.1 and later, reduces the number of chunks in the output. The esbuild code-splitting algorithm can produce many small chunks that cause network thrashing. The chunk optimizer merges the chunks to reduce the number of requests. The @rx-angular/rebundle package provides a configurable alternative for projects that need more control over the chunking .
Complete Example Session
This session demonstrates the production build, the budget configuration, and the bundle analysis.
// ============================================
// PART 1: THE BUDGET CONFIGURATION
// ============================================
// angular.json
{
"projects": {
"my-app": {
"architect": {
"build": {
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractLicenses": true,
"buildOptimizer": true
}
}
}
}
}
}
}
// ============================================
// PART 2: THE PRODUCTION BUILD
// ============================================
// ng build --configuration production
// Output:
// ✔ Browser application bundle generation complete.
// Initial total | 380.23 kB
// Lazy | 120.45 kB
// - dashboard | 80.12 kB
// - admin | 40.33 kB
// Build at 2026-10-02T12:00:00.000Z
// The build succeeds because the initial bundle is under 500 kB.
// ============================================
// PART 3: THE BUDGET WARNING
// ============================================
// Add a large library.
// npm install large-library
// ng build --configuration production
// Output:
// ✔ Browser application bundle generation complete.
// Initial total | 620.45 kB
// Warning: bundle initial exceeded maximum budget.
// Budget 500.00 kB was not met by 120.45 kB.
// The build succeeds but the warning is printed.
// ============================================
// PART 4: THE BUDGET ERROR
// ============================================
// Add more code.
// Initial total | 1.2 MB
// ng build --configuration production
// Output:
// ✖ Browser application bundle generation failed.
// Error: bundle initial exceeded maximum budget.
// Budget 1.00 MB was not met by 200.00 kB.
// The build fails. The deployment is blocked.
// ============================================
// PART 5: THE BUNDLE ANALYSIS
// ============================================
// ng build --configuration production --stats-json
// npx webpack-bundle-analyzer dist/my-app/stats.json
// The analyzer shows:
// - @angular/core: 180 kB
// - rxjs: 120 kB
// - large-library: 200 kB
// - app code: 80 kB
// The large library is the optimization opportunity.
// ============================================
// PART 6: THE LAZY LOADING FIX
// ============================================
// Before:
import { DashboardComponent } from './dashboard/dashboard.component';
// After:
const routes: Routes = [
{
path: 'dashboard',
loadComponent: () => import('./dashboard/dashboard.component').then(m => m.DashboardComponent),
},
];
// The dashboard component is moved out of the initial bundle.
// The initial bundle shrinks. The dashboard chunk loads on navigation.
// ============================================
// PART 7: THE isolatedModules OPTIMIZATION
// ============================================
// tsconfig.json
{
"compilerOptions": {
"isolatedModules": true,
"useDefineForClassFields": true
}
}
// The build is faster.
// The bundler transpiles the TypeScript code.
// The Babel-based optimization passes are removed.
// ============================================
// PART 8: THE CHUNK OPTIMIZER
// ============================================
// angular.json
{
"optimization": {
"scripts": true,
"styles": true,
"fonts": true
},
"experimentalChunkOptimizer": true
}
// The chunk optimizer reduces the number of chunks.
// The network requests are reduced.
// The LCP is improved.
// ============================================
// PART 9: THE BUILD OUTPUT
// ============================================
// dist/my-app/browser/
// ├── index.html
// ├── main-abc123.js (initial bundle)
// ├── polyfills-def456.js (polyfills)
// ├── styles-ghi789.css (global styles)
// ├── dashboard-jkl012.js (lazy chunk)
// └── admin-mno345.js (lazy chunk)
// The hashes in the file names are the output hashing.
// The browser caches the files by hash.
// ============================================
// PART 10: THE CHECKLIST
// ============================================
// 1. Use ng build --configuration production
// 2. Configure budgets in angular.json
// 3. Analyze the bundle with --stats-json
// 4. Lazy-load the routes that are not on the initial page
// 5. Enable isolatedModules for faster builds
// 6. Use the chunk optimizer for fewer requests
// 7. Monitor the bundle size in CI
// 8. Fix the budget warnings before they become errors
The ten parts cover the budget configuration, the production build, the budget warning, the budget error, the bundle analysis, the lazy loading fix, the isolatedModules optimization, the chunk optimizer, the build output, and the checklist.
Quick Reference
The Production Build Optimizations
| Optimization | Purpose |
|---|---|
| AOT compilation | Compile templates at build time |
| Minification | Remove whitespace and shorten names |
| Tree-shaking | Remove unused code |
| Dead-code elimination | Remove unreachable code |
| Output hashing | Enable safe caching |
| Source map control | Disable or hide source maps |
The Budget Types
| Type | Purpose |
|---|---|
initial | JavaScript and CSS for bootstrapping |
bundle | A specific bundle |
allScript | All scripts |
all | The entire application |
anyComponentStyle | Any component stylesheet |
anyScript | Any script |
any | Any file |
The Budget Properties
| Property | Purpose |
|---|---|
maximumWarning | Warning threshold |
maximumError | Error threshold |
minimumWarning | Minimum warning |
minimumError | Minimum error |
baseline | Baseline for comparison |
The Size Formats
| Format | Meaning |
|---|---|
123 or 123b | Bytes |
123kb | Kilobytes |
123mb | Megabytes |
12% | Percentage of baseline |
The Build Analysis Commands
| Command | Purpose |
|---|---|
ng build --stats-json | Produce the statistics file |
npx webpack-bundle-analyzer | Render the treemap |
ng build --configuration production | Production build |
Best Practices
✅ Do This:
// Set budgets in angular.json
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
}
# Use the production configuration
ng build --configuration production
# Analyze the bundle
ng build --stats-json && npx webpack-bundle-analyzer dist/stats.json
// Enable isolatedModules for faster builds
{
"compilerOptions": {
"isolatedModules": true
}
}
❌ Don’t Do This:
# Don't deploy the development build
ng build # without --configuration production
// Don't set the budget too high
{
"maximumError": "10mb" // defeats the purpose
}
# Don't ignore the budget warnings
# The warnings become errors
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Bundle too large | No lazy loading | Lazy-load routes |
| Budget exceeded | Large dependency | Analyze and replace |
| Slow build | No isolatedModules | Enable the option |
| Too many chunks | esbuild splitting | Use the chunk optimizer |
| Stale cache | No output hashing | Enable output hashing |
Real-World Examples
1. Production Build
ng build --configuration production
2. Budget Configuration
{ "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" }
3. Bundle Analysis
ng build --stats-json && npx webpack-bundle-analyzer dist/stats.json
4. Lazy Loading
loadComponent: () => import('./dashboard.component').then(m => m.DashboardComponent)
5. isolatedModules
{ "compilerOptions": { "isolatedModules": true } }
6. Output Hashing
{ "outputHashing": "all" }
7. Source Maps
{ "sourceMap": false }
8. Build Optimizer
{ "buildOptimizer": true }
9. Chunk Optimizer
{ "experimentalChunkOptimizer": true }
10. CI Check
- run: ng build --configuration production
Visual
The Production Build Pipeline
┌──────────────────────────────────────────────┐
│ SOURCE CODE │
│ │ │
│ ▼ │
│ AOT COMPILATION │
│ └─ Templates compiled at build time │
│ │ │
│ ▼ │
│ TREE-SHAKING │
│ └─ Unused code removed │
│ │ │
│ ▼ │
│ MINIFICATION │
│ └─ Whitespace and names shortened │
│ │ │
│ ▼ │
│ OUTPUT HASHING │
│ └─ File names include content hash │
│ │ │
│ ▼ │
│ BUNDLE │
│ └─ Small, optimized, cacheable │
│ │
└──────────────────────────────────────────────┘
The Budget Thresholds
┌──────────────────────────────────────────────┐
│ BUDGET THRESHOLDS │
│ │
│ maximumWarning: 500kb │
│ └─ Build warns │
│ │
│ maximumError: 1mb │
│ └─ Build fails │
│ │
│ The build succeeds between the thresholds. │
│ The build fails above the error threshold. │
│ │
└──────────────────────────────────────────────┘
The Bundle Analysis
┌──────────────────────────────────────────────┐
│ BUNDLE ANALYSIS │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ @angular/core (180 kB) │ │
│ ├────────────────────────────────────────┤ │
│ │ large-library (200 kB) ← opportunity │ │
│ ├────────────────────────────────────────┤ │
│ │ rxjs (120 kB) │ │
│ ├────────────────────────────────────────┤ │
│ │ app code (80 kB) │ │
│ └────────────────────────────────────────┘ │
│ │
│ The large blocks are the targets. │
│ │
└──────────────────────────────────────────────┘
The Build Output
┌──────────────────────────────────────────────┐
│ dist/my-app/browser/ │
│ ├── index.html │
│ ├── main-abc123.js (initial bundle) │
│ ├── polyfills-def456.js (polyfills) │
│ ├── styles-ghi789.css (global styles) │
│ ├── dashboard-jkl012.js (lazy chunk) │
│ └── admin-mno345.js (lazy chunk) │
│ │
│ The hashes enable safe caching. │
│ │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Production build | ng build --configuration production |
| AOT compilation | Templates compiled at build time |
| Tree-shaking | Unused code removed |
| Minification | Whitespace and names shortened |
| Output hashing | File names include content hash |
| Budget types | initial, bundle, all, anyComponentStyle |
| Budget defaults | 500 kB warning, 1 MB error for initial |
| Build analysis | --stats-json, bundle analyzer |
isolatedModules | Faster build, up to 10% |
| Chunk optimizer | Fewer chunks, fewer requests |
Key takeaways:
- The
ng build --configuration productioncommand enables the production optimizations. The build includes AOT compilation, minification, tree-shaking, dead-code elimination, and output hashing. The development build does not . - AOT compilation compiles the templates at build time. The Angular compiler is removed from the bundle. The bundle is smaller and the rendering is faster. The template errors are caught at build time .
- The
budgetsarray inangular.jsonsets size thresholds. Theinitialbudget covers the JavaScript and CSS for bootstrapping. TheanyComponentStylebudget covers the component stylesheets. The build warns at the warning threshold and fails at the error threshold . - The bundle analysis identifies the optimization opportunities. The
--stats-jsonflag produces the statistics file. Thewebpack-bundle-analyzerrenders the treemap. The large blocks are the targets . - The
isolatedModulesoption makes the build faster. The bundler transpiles the TypeScript code instead of TypeScript. The Babel-based optimization passes are removed. The build is up to 10% faster . - The chunk optimizer reduces the number of chunks. The esbuild splitting algorithm can produce many small chunks. The chunk optimizer merges them. The network requests are reduced and the LCP is improved .
- The budget is the guardrail. Without it, the bundle grows silently. With it, the team is notified on the build that introduces the growth. The warning is the opportunity to investigate. The error is the block that prevents the deployment of an oversized application .
Remember: The production build is not the development build with a flag. It is a different artifact. The optimizations reduce the bundle by 50–80%. The AOT compilation removes the compiler. The tree-shaking removes the unused code. The minification removes the whitespace. The output hashing enables the caching. The budgets set the limits. The analysis identifies the opportunities. The isolatedModules makes the build faster. The chunk optimizer makes the delivery faster. The production build is the artifact the user receives.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!