Production and Performance

This section documents modern, standalone Angular — signals, the built-in @if / @for / @switch control flow, @defer, typed reactive forms, provideHttpClient, functional guards and interceptors, and server-side rendering with hydration — as described by the official documentation at angular.dev, which is the reference these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against angular.dev before being relied on in production. Angular ships a major release roughly every six months and its APIs continue to evolve: the examples here target the current major release; where a consulted source disagrees with the current documentation, the documentation wins and the difference is noted.

This section’s bibliography lists the reference material consulted while preparing these pages.

Shipping an Angular app well means an optimized production build with enforced size budgets, attention to the Core Web Vitals, and — where SEO or first paint matter — server-side rendering with hydration. See ng build and Performance.

Production builds

ng build uses the production configuration by default: ahead-of-time template compilation, minification, tree-shaking, dead-code elimination, and output hashing (main.<hash>.js) for long-term caching. Output lands in dist/<project>/.

ng build                             # production by default
ng build --configuration development
ng build --source-map                # emit .map files for debugging
ng build --stats-json                # for bundle analysis (esbuild --analyze, source-map-explorer)
ng deploy                            # via a deploy builder added with `ng add`

Configurations live in angular.json under projects.<name>.architect.build.configurations. Each one can set optimization flags and a file replacement, so environment-specific values are swapped at build time:

{
  "configurations": {
    "production": {
      "optimization": true,
      "outputHashing": "all",
      "sourceMap": false,
      "fileReplacements": [
        {
          "replace": "src/environments/environment.ts",
          "with": "src/environments/environment.prod.ts"
        }
      ],
      "budgets": [
        { "type": "initial", "maximumWarning": "500kB", "maximumError": "1MB" },
        { "type": "anyComponentStyle", "maximumWarning": "4kB", "maximumError": "8kB" }
      ]
    }
  }
}

Bundle budgets fail the build when a bundle grows past maximumError — the cheapest guard against accidental bloat such as a mis-imported library. Budget types include initial, all, anyComponentStyle, bundle, and allScript. See Workspace configuration.

Deployment: ng deploy runs a deploy builder contributed by a provider package (ng add @angular/fire, angular-cli-ghpages, and others). Without one, copy dist/<project>/browser/ to any static host and add a SPA fallback that serves index.html for unknown paths. See Deployment.

Core Web Vitals

Metric Measures Angular levers

LCP — Largest Contentful Paint

how fast the main content loads

NgOptimizedImage with priority, SSR / SSG, @defer for below-the-fold content, route-level code splitting

INP — Interaction to Next Paint

responsiveness to input

OnPush and signals, zoneless change detection, withEventReplay(), smaller bundles

CLS — Cumulative Layout Shift

visual stability

explicit width / height on images (required by NgOptimizedImage), reserving space for deferred and async content

NgOptimizedImage — replace src with ngSrc and Angular sets loading / fetchpriority, generates a srcset, requires an explicit width / height (or fill), and warns on oversized files:

<img ngSrc="/assets/hero.jpg" width="1200" height="600" priority alt="Product hero" />
<img ngSrc="/assets/thumb.jpg" width="200" height="200" alt="Thumbnail" />

@defer loads a block’s JavaScript only when a trigger fires (on viewport, on interaction, on idle), shrinking the initial bundle — recap on Control flow and @defer. Route-level code splitting with loadComponent / loadChildren keeps each route’s code out of the initial download — recap on Routing. See Performance.

SSR and hybrid rendering

Add SSR with ng add @angular/ssr (or ng new --ssr). It scaffolds a server.ts request handler, a main.server.ts, and an app.config.server.ts. Rendering can be mixed per route: client-side, server-side (SSR) per request, and prerendered (SSG) at build time.

// app.config.ts — hydrate the server-rendered markup on the client
import { ApplicationConfig } from '@angular/core';
import {
  provideClientHydration, withEventReplay, withIncrementalHydration,
} from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
  providers: [
    provideClientHydration(withEventReplay(), withIncrementalHydration()),
  ],
};

provideClientHydration() makes the client reuse the server-rendered DOM instead of re-creating it — no flicker and no duplicate data fetch. withEventReplay() records clicks and keystrokes that happen before hydration completes and replays them once it does.

Incremental hydration hydrates @defer blocks lazily, each on its own trigger, using the hydrate keyword: the server renders the content, and the client ships and activates its JavaScript only when needed. It requires opting in with withIncrementalHydration() (as above); without it the hydrate triggers are inert and the block behaves as a plain @defer.

@defer (hydrate on viewport) {
  <app-reviews [productId]="id()" />
} @placeholder {
  <p>Reviews</p>
}

Triggers: hydrate on idle, hydrate on viewport, hydrate on interaction, hydrate on hover, hydrate on immediate, hydrate on timer(…​), hydrate when <expr>, and hydrate never for content that stays permanently static. See Incremental hydration.

Prerendering (SSG) renders routes to static HTML at build time — set a route’s renderMode to RenderMode.Prerender in the server routes config and list its parameter values. The app-shell pattern prerenders a minimal layout (header, nav, spinner) as the first paint while the full app bootstraps: ng generate app-shell. See Server-side rendering and Hydration.

The SSR request flow

sequenceDiagram participant B as Browser participant S as Angular server B->>S: GET /products/7 S->>S: render the component tree to HTML S-->>B: serialized HTML + inlined transfer state Note over B: browser paints immediately (fast FCP / LCP) B->>B: bootstrap and hydrate (reuse DOM, replay events) Note over B: page is fully interactive Note over B: a hydrate-on-viewport island scrolls into view B->>S: request the island's JS chunk S-->>B: JS chunk B->>B: hydrate that deferred island only

Cross-links: Control flow and @defer, Lifecycle and change detection for zoneless change detection, and CORS for cross-origin data fetching on the server.