Angular · Production

Production Build

ng build turns your Angular app into small, optimised static files. A few settings — environments, base href, budgets and a server fallback — make sure it works the same in production as on your machine.

Advanced 6 min Lesson 36 of 38Examples target Angular 20 with standalone components. Each lesson states the version it was verified against.
Before you start:Angular CLI
0/38 in Angular
On this page · 6 min · Advanced

Learning objectives#

  • Create a production build and know its output
  • Use environment files for configuration
  • Set base href and budgets
  • Deploy to static hosting with route fallback

Explanation#

ng build uses the production configuration by default: ahead-of-time compilation, tree-shaking, minification, hashed file names. Output: dist/<project>/browser.

Environment files: generate with ng g environments; angular.json fileReplacements swaps environment.ts for environment.development.ts in dev builds. Put the API URL and feature flags there — never secrets.

Serving from a sub-folder? Build with ng build --base-href /shop/ so asset and route URLs are correct.

Budgets in angular.json warn or fail the build when the initial bundle grows past a limit — a guard against accidental huge dependencies.

Any static host works (Hostinger, Azure Static Web Apps, Netlify, S3 + CloudFront). Configure a fallback so every unknown path serves index.html, or deep links like /products/7 return 404.

Need SEO-friendly HTML or faster first paint? Angular SSR/prerendering (ng add @angular/ssr) renders pages on the server.

Syntax#

Shell Illustrative
ng build                         # production by default → dist/shop-app/browser
ng build --base-href /shop/      # when hosted in a sub-folder
npx http-server dist/shop-app/browser -p 8080   # quick local check

Code example: Environments and a production-safe API URL#

TypeScriptenvironment.ts Illustrative
// src/environments/environment.ts            (used by ng build — production)
export const environment = {
  production: true,
  apiUrl: 'https://api.shop.example.com',
  featureNewCheckout: false,
};


// src/environments/environment.development.ts (swapped in by angular.json for ng serve)
// export const environment = { production: false, apiUrl: '/api', featureNewCheckout: true };


// src/app/app.config.ts
import { ApplicationConfig, InjectionToken } from '@angular/core';
import { provideHttpClient, withFetch } from '@angular/common/http';


export const API_URL = new InjectionToken<string>('API_URL');


export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch()),
    { provide: API_URL, useValue: environment.apiUrl },
  ],
};


/* angular.json (excerpt)
"budgets": [
  { "type": "initial", "maximumWarning": "500kB", "maximumError": "1MB" },
  { "type": "anyComponentStyle", "maximumWarning": "4kB", "maximumError": "8kB" }
]


public_html/.htaccess (Apache / Hostinger) — route fallback
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]
*/
Expected output
ng build prints the initial and lazy chunks with sizes, e.g. “main-AB12CD.js 210 kB | Estimated transfer 62 kB” and “Lazy chunk files: chunk-EF34.js (products) 34 kB”, and fails if a budget’s error limit is exceeded. Uploading dist/shop-app/browser plus the .htaccess file makes deep links like /products/7 work.

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. environment.ts holds production settings; the development file replaces it for ng serve.
  2. The API URL is provided through an InjectionToken, so services do not hard-code it.
  3. Budgets stop the build when bundles grow beyond agreed limits.
  4. The output to deploy is the browser folder inside dist.
  5. The .htaccess rules send unknown paths to index.html so the router can handle them.

Real-world use case#

A team deploys the same build artefact to staging and production on Azure Static Web Apps; a staticwebapp.config.json navigationFallback replaces .htaccess, and environment-specific API URLs come from the build configuration.

Common mistakes#

Secrets in environment.ts

Everything in the bundle is public. Keep secrets on the server.

Deploying the wrong folder

Upload the contents of dist/<project>/browser, not the dist folder itself.

Deep links return 404

Static hosts look for real files. Configure the index.html fallback.

Best practices#

  • Build once, configure per environment
  • Keep budgets tight
  • Set base href for sub-folders
  • Configure route fallback on the host

Remember this#

ng build → dist/<project>/browser.

environment files = public config only.

Static hosting needs an index.html fallback.

Practice exercise#

Your task: Write the command to build for a site served at https://example.com/portal/ and the matching Apache fallback rule.

Need a hint?
--base-href /portal/ and RewriteBase /portal/ with fallback to /portal/index.html.
Show solution
Shell Illustrative
ng build --base-href /portal/


# public_html/portal/.htaccess
# RewriteEngine On
# RewriteBase /portal/
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^ /portal/index.html [L]

Mini quiz#

Production Build quizMultiple choice Question 1 of 3

Which folder do you upload after ng build in Angular 17+?

Score so far: 0/0

Want more? Practise all Angular MCQs · Angular interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.