Angular 61 🅰️ Bootstrapping Without NgModule
The previous chapter covered standalone components, and this chapter covers the bootstrap that goes with them. Before the standalone API, every Angular application started with an NgModule — the AppModule — and the platformBrowserDynamic().bootstrapModule(AppModule) call. The standalone API replaced the root module with the bootstrapApplication function and the ApplicationConfig object, and it made the providers explicit. This chapter covers the main.ts‘s bootstrap, the ApplicationConfig‘s providers, the provideX functions, the provideRouter‘s routes, the provideHttpClient‘s configuration, the provideZoneChangeDetection, the provideAnimations, the provideExperimentalZonelessChangeDetection‘s zoneless, the bootstrapApplication‘s options, the error’s handling, the environment’s configuration, and the patterns that make the bootstrap maintainable. It builds on Angular 59 and 60 and prepares the ground for the more advanced’s DI’s material.
Key point: The bootstrapApplication(AppComponent, appConfig) bootstraps the root component directly, and the appConfig is the ApplicationConfig object with the providers array. The provideX functions — the provideRouter, the provideHttpClient, the provideAnimations, the provideZoneChangeDetection — configure the framework’s features. The provideRouter(routes) provides the router, and the routes array is the application’s. The provideHttpClient() provides the HTTP, and the withInterceptors‘s feature adds the interceptors. The provideZoneChangeDetection‘s is the zone’s, and the provideExperimentalZonelessChangeDetection‘s is the zoneless’s. The bootstrapApplication‘s second argument is the ApplicationConfig, and the third is the options. The catch handles the bootstrap’s error, and the console.error is the common’s.
Why the bootstrap without the NgModule
The NgModule’s bootstrap required the AppModule, the declarations, the imports, the bootstrap, and the platformBrowserDynamic. The five were the ceremony’s, and the ceremony’s is the overhead’s.
The NgModule’s bootstrap. The classic’s bootstrap was the platformBrowserDynamic().bootstrapModule(AppModule), and the AppModule was the root’s.
// The classic's main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.error(err));
The platformBrowserDynamic().bootstrapModule(AppModule) is the classic’s, and the classic’s is the NgModule’s. The AppModule is the root’s, and the root’s is the module’s. The two are the pair, and the pair is the legacy’s.
The standalone’s bootstrap. The modern’s bootstrap is the bootstrapApplication(AppComponent, appConfig), and the AppComponent is the root’s, and the appConfig is the providers’s.
// The modern's main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
The bootstrapApplication(AppComponent, appConfig) is the modern’s, and the modern’s is the standalone’s. The AppComponent is the root’s, and the appConfig is the providers’s. The two are the pair, and the pair is the modern’s.
Why the standalone’s bootstrap matters. The standalone’s bootstrap is the direct’s, and the direct’s is the simpler’s. The root module’s is the indirect’s, and the indirect’s is the ceremony’s. The two are the pair, and the pair is the modern’s.
Why the ApplicationConfig matters. The ApplicationConfig is the object’s, and the object’s is the providers’s. The providers array is the list’s, and the list’s is the framework’s. The two are the pair, and the pair is the design’s.
Why the provideX matters. The provideX is the function’s, and the function’s is the concise’s. The provideRouter, the provideHttpClient, the provideAnimations are the three, and the three are the framework’s. The two are the pair, and the pair is the modern’s.
Why the bootstrapApplication‘s error matters. The bootstrapApplication‘s error is the promise’s, and the promise’s is the catch’s. The catch((err) => console.error(err)) is the common’s, and the common’s is the safety’s. The two are the pair, and the pair is the design’s.
Why the standalone’s bootstrap should be the default. The standalone’s bootstrap should be the default, and the default’s is the modern’s. The Angular 15’s is the standalone’s, and the standalone’s is the standard’s. The two are the pair, and the pair is the recommendation’s.
Why the legacy’s bootstrap matters. The legacy’s bootstrap matters for the migration’s, and the migration’s is the incremental’s. The NgModule’s is the legacy’s, and the legacy’s is the compatibility’s. The two are the pair, and the pair is the transition’s.
Why the bootstrap is the entry’s. The bootstrap is the entry’s, and the entry’s is the application’s. The
main.tsis the file’s, and the file’s is the first’s. The two are the pair, and the pair is the start’s.
The main.ts‘s bootstrap
The main.ts is the entry’s, and the entry’s is the bootstrapApplication‘s.
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
The main.ts is the entry’s, and the entry’s is the bootstrapApplication‘s. The AppComponent is the root’s, and the appConfig is the providers’s. The two are the pair, and the pair is the modern’s.
Why the main.ts matters. The main.ts is the entry’s, and the entry’s is the first’s. The bootstrapApplication is the call’s, and the call’s is the application’s. The two are the pair, and the pair is the start’s.
The AppComponent‘s import. The AppComponent is the import’s, and the import’s is the root’s.
import { AppComponent } from './app/app.component';
The import { AppComponent } is the import’s, and the import’s is the root’s. The two are the pair, and the pair is the pattern’s.
The appConfig‘s import. The appConfig is the import’s, and the import’s is the config’s.
import { appConfig } from './app/app.config';
The import { appConfig } is the import’s, and the import’s is the config’s. The two are the pair, and the pair is the pattern’s.
Why the main.ts‘s structure matters. The main.ts‘s structure is the entry’s, and the entry’s is the simple’s. The imports, the bootstrap, the catch are the three, and the three are the entry’s. The two are the pair, and the pair is the design’s.
The catch‘s handling. The catch is the error’s, and the error’s is the console’s.
.catch((err) => console.error(err));
The .catch((err) => console.error(err)) is the error’s, and the error’s is the console’s. The two are the pair, and the pair is the safety’s.
Why the catch matters. The catch is the bootstrap’s, and the bootstrap’s is the promise’s. The bootstrapApplication returns the promise’s, and the promise’s is the catch’s. The two are the pair, and the pair is the design’s.
Why the main.ts should be the minimal. The main.ts should be the minimal, and the minimal’s is the simple’s. The bootstrap’s is the entry’s, and the entry’s is the three’s. The two are the pair, and the pair is the design’s.
Why the main.ts should be the stable. The main.ts should be the stable, and the stable’s is the rare’s. The main.ts is the change’s, and the change’s is the never’s. The two are the pair, and the pair is the design’s.
The ApplicationConfig
The ApplicationConfig is the object’s, and the object’s is the providers’s.
// app.config.ts
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(),
],
};
The appConfig is the object’s, and the object’s is the ApplicationConfig‘s. The providers array is the list’s, and the list’s is the framework’s. The two are the pair, and the pair is the modern’s.
Why the ApplicationConfig matters. The ApplicationConfig is the providers’s, and the providers’s is the explicit’s. The NgModule’s providers is the legacy’s, and the legacy’s is the module’s. The two are the pair, and the pair is the modern’s.
Why the app.config.ts matters. The app.config.ts is the file’s, and the file’s is the config’s. The app.config.ts is the convention’s, and the convention’s is the CLI’s. The two are the pair, and the pair is the pattern’s.
Why the providers array matters. The providers array is the list’s, and the list’s is the framework’s. The order’s is the declaration’s, and the declaration’s is the array’s. The two are the pair, and the pair is the design’s.
Why the provideZoneChangeDetection matters. The provideZoneChangeDetection({ eventCoalescing: true }) is the zone’s, and the zone’s is the coalescing’s.
provideZoneChangeDetection({ eventCoalescing: true })
The provideZoneChangeDetection is the zone’s, and the zone’s is the coalescing’s. The eventCoalescing: true is the coalescing’s, and the coalescing’s is the performance’s. The two are the pair, and the pair is the optimization’s.
Why the eventCoalescing matters. The eventCoalescing is the events’s, and the events’s is the batch’s. The coalescing’s is the performance’s, and the performance’s is the change detection’s. The two are the pair, and the pair is the design’s.
Why the appConfig should be the exported. The appConfig should be the exported, and the exported’s is the main’s. The main.ts imports the appConfig, and the appConfig is the bootstrap’s. The two are the pair, and the pair is the pattern’s.
Why the appConfig should be the separate. The appConfig should be the separate, and the separate’s is the app.config.ts‘s. The main.ts is the bootstrap’s, and the app.config.ts is the providers’s. The two are the pair, and the pair is the organization’s.
The provideRouter
The provideRouter(routes) provides the router, and the routes array is the application’s.
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: '',
loadComponent: () => import('./home/home.component').then((m) => m.HomeComponent),
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes').then((m) => m.adminRoutes),
},
];
The routes is the array’s, and the array’s is the Routes‘s. The loadComponent and the loadChildren are the lazy’s, and the lazy’s is the bundle’s. The two are the pair, and the pair is the modern’s.
Why the provideRouter matters. The provideRouter is the router’s, and the router’s is the function’s. The RouterModule.forRoot is the legacy’s, and the legacy’s is the module’s. The two are the pair, and the pair is the modern’s.
Why the routes matters. The routes is the array’s, and the array’s is the application’s. The loadComponent and the loadChildren are the lazy’s, and the lazy’s is the performance’s. The two are the pair, and the pair is the design’s.
Why the provideRouter‘s features matter. The provideRouter‘s features are the withComponentInputBinding, the withRouterConfig, the withInMemoryScrolling, the withViewTransitions, the withDebugTracing.
provideRouter(
routes,
withComponentInputBinding(),
withInMemoryScrolling({ scrollPositionRestoration: 'enabled' }),
)
The withComponentInputBinding is the input’s, and the withInMemoryScrolling is the scroll’s. The two are the pair, and the pair is the feature’s.
Why the withComponentInputBinding matters. The withComponentInputBinding binds the route’s params to the component’s inputs, and the binding’s is the modern’s. The @Input‘s is the classic’s, and the classic’s is the ActivatedRoute‘s. The two are the pair, and the pair is the design’s.
Why the provideRouter‘s withViewTransitions matters. The withViewTransitions is the view’s, and the view’s is the transition’s.
provideRouter(routes, withViewTransitions())
The withViewTransitions() is the transition’s, and the transition’s is the modern’s. The two are the pair, and the pair is the design’s.
Why the provideRouter should be the single’s. The provideRouter should be the single’s, and the single’s is the application’s. The provideRouter is the once’s, and the once’s is the root’s. The two are the pair, and the pair is the pattern’s.
Why the provideRouter‘s order matters. The provideRouter‘s order is the features’s, and the features’s is the declaration’s. The provideRouter(routes, withComponentInputBinding(), withViewTransitions()) is the order’s, and the order’s is the array’s. The two are the pair, and the pair is the design’s.
The provideHttpClient
The provideHttpClient() provides the HTTP, and the withInterceptors‘s feature adds the interceptors.
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { authInterceptor } from './interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([authInterceptor]),
),
],
};
The provideHttpClient() is the HTTP’s, and the HTTP’s is the provider’s. The withInterceptors([authInterceptor]) is the interceptor’s, and the interceptor’s is the auth’s. The two are the pair, and the pair is the modern’s.
Why the provideHttpClient matters. The provideHttpClient is the HTTP’s, and the HTTP’s is the function’s. The HttpClientModule is the legacy’s, and the legacy’s is the module’s. The two are the pair, and the pair is the modern’s.
Why the withInterceptors matters. The withInterceptors is the interceptor’s, and the interceptor’s is the functional’s. The HTTP_INTERCEPTORS is the legacy’s, and the legacy’s is the class’s. The two are the pair, and the pair is the modern’s.
Why the withFetch matters. The withFetch() is the fetch’s, and the fetch’s is the modern’s.
provideHttpClient(withFetch())
The withFetch() is the fetch’s, and the fetch’s is the HTTP’s. The XMLHttpRequest is the legacy’s, and the legacy’s is the default’s. The two are the pair, and the pair is the modern’s.
Why the withFetch matters. The withFetch is the fetch’s, and the fetch’s is the modern’s. The fetch’s is the performance’s, and the performance’s is the SSE’s and the streaming’s. The two are the pair, and the pair is the design’s.
Why the withRequestsMadeViaParent matters. The withRequestsMadeViaParent() is the parent’s, and the parent’s is the child’s.
provideHttpClient(withRequestsMadeViaParent())
The withRequestsMadeViaParent() is the parent’s, and the parent’s is the nested’s. The two are the pair, and the pair is the design’s.
Why the provideHttpClient‘s order matters. The provideHttpClient‘s order is the features’s, and the features’s is the declaration’s. The provideHttpClient(withFetch(), withInterceptors([...])) is the order’s, and the order’s is the array’s. The two are the pair, and the pair is the design’s.
Why the provideHttpClient should be the single’s. The provideHttpClient should be the single’s, and the single’s is the application’s. The provideHttpClient is the once’s, and the once’s is the root’s. The two are the pair, and the pair is the pattern’s.
The provideAnimations and the provideZoneChangeDetection
The two are the framework’s, and the framework’s is the change detection’s.
The provideAnimations. The provideAnimations() is the animations’s, and the animations’s is the browser’s.
import { provideAnimations } from '@angular/platform-browser/animations';
export const appConfig: ApplicationConfig = {
providers: [
provideAnimations(),
],
};
The provideAnimations() is the animations’s, and the animations’s is the browser’s. The BrowserAnimationsModule is the legacy’s, and the legacy’s is the module’s. The two are the pair, and the pair is the modern’s.
Why the provideAnimations matters. The provideAnimations is the animation’s, and the animation’s is the Material’s. The Angular Material’s is the animation’s, and the animation’s is the dependency’s. The two are the pair, and the pair is the design’s.
The provideNoopAnimations. The provideNoopAnimations() is the animations’s no-op’s, and the no-op’s is the test’s.
provideNoopAnimations()
The provideNoopAnimations() is the no-op’s, and the no-op’s is the test’s. The two are the pair, and the pair is the design’s.
Why the provideNoopAnimations matters. The provideNoopAnimations is the test’s, and the test’s is the deterministic’s. The two are the pair, and the pair is the design’s.
The provideZoneChangeDetection. The provideZoneChangeDetection({ eventCoalescing: true }) is the zone’s, and the zone’s is the coalescing’s.
provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true })
The provideZoneChangeDetection is the zone’s, and the zone’s is the coalescing’s. The eventCoalescing and the runCoalescing are the two, and the two are the options’s. The two are the pair, and the pair is the design’s.
Why the provideZoneChangeDetection matters. The provideZoneChangeDetection is the zone’s, and the zone’s is the default’s. The provideExperimentalZonelessChangeDetection is the zoneless’s, and the zoneless’s is the future’s. The two are the pair, and the pair is the design’s.
The provideExperimentalZonelessChangeDetection. The provideExperimentalZonelessChangeDetection() is the zoneless’s, and the zoneless’s is the experimental’s.
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
],
};
The provideExperimentalZonelessChangeDetection() is the zoneless’s, and the zoneless’s is the experimental’s. The two are the pair, and the pair is the future’s.
Why the zoneless matters. The zoneless is the zone’s removal’s, and the removal’s is the performance’s. The zoneless’s is the signals’s, and the signals’s is the modern’s. The two are the pair, and the pair is the design’s.
Why the zoneless should be the careful. The zoneless should be the careful, and the careful’s is the experimental’s. The zoneless’s is the future’s, and the future’s is the migration’s. The two are the pair, and the pair is the design’s.
The bootstrapApplication‘s options
The bootstrapApplication‘s third argument is the options, and the options’s is the context’s.
bootstrapApplication(AppComponent, appConfig, {
// The options
});
The bootstrapApplication(AppComponent, appConfig, { ... }) is the three’s, and the three’s is the options’s. The options’s is the context’s, and the context’s is the application’s. The two are the pair, and the pair is the design’s.
The ApplicationRef‘s example. The ApplicationRef‘s example is the bootstrapApplication‘s options’s.
bootstrapApplication(AppComponent, appConfig);
// The ApplicationRef is available via the inject.
The ApplicationRef is the inject’s, and the inject’s is the context’s. The two are the pair, and the pair is the pattern’s.
Why the options matter. The options is the advanced’s, and the advanced’s is the rare’s. The bootstrapApplication‘s options is the context’s, and the context’s is the specific’s. The two are the pair, and the pair is the design’s.
The errorHandler‘s replacement. The errorHandler‘s replacement is the provider’s, and the provider’s is the ErrorHandler‘s.
export const appConfig: ApplicationConfig = {
providers: [
{ provide: ErrorHandler, useClass: GlobalErrorHandler },
],
};
The { provide: ErrorHandler, useClass: GlobalErrorHandler } is the replacement’s, and the replacement’s is the global’s. The two are the pair, and the pair is the pattern’s.
Why the errorHandler matters. The errorHandler is the global’s, and the global’s is the application’s. The GlobalErrorHandler is the custom’s, and the custom’s is the logging’s. The two are the pair, and the pair is the design’s.
The enableProdMode‘s example. The enableProdMode() is the production’s, and the production’s is the performance’s.
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
The enableProdMode() is the production’s, and the production’s is the performance’s. The two are the pair, and the pair is the pattern’s.
Why the enableProdMode matters. The enableProdMode is the production’s, and the production’s is the performance’s. The development’s is the checks’s, and the checks’s is the verbose’s. The two are the pair, and the pair is the design’s.
Why the options should be the sparing. The options should be the sparing, and the sparing’s is the specific’s. The options’s is the advanced’s, and the advanced’s is the rare’s. The two are the pair, and the pair is the design’s.
The bootstrap’s patterns
The patterns are the common’s, and the common’s is the practice’s.
The pattern 1: the minimal’s main.ts. The minimal’s main.ts is the three’s, and the three’s is the imports’s.
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
The minimal’s main.ts is the three’s, and the three’s is the imports’s. The two are the pair, and the pair is the pattern’s.
Why the pattern 1 matters. The pattern 1 is the minimal’s, and the minimal’s is the design’s. The main.ts is the three’s, and the three’s is the stable’s. The two are the pair, and the pair is the design’s.
The pattern 2: the app.config.ts. The app.config.ts is the providers’s, and the providers’s is the framework’s.
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor])),
provideAnimations(),
],
};
The app.config.ts is the providers’s, and the providers’s is the framework’s. The two are the pair, and the pair is the pattern’s.
Why the pattern 2 matters. The pattern 2 is the app.config.ts‘s, and the app.config.ts‘s is the separation’s. The main.ts is the bootstrap’s, and the app.config.ts is the providers’s. The two are the pair, and the pair is the organization’s.
The pattern 3: the app.routes.ts. The app.routes.ts is the routes’s, and the routes’s is the array’s.
export const routes: Routes = [
{ path: '', loadComponent: () => import('./home/home.component').then((m) => m.HomeComponent) },
{ path: 'admin', loadChildren: () => import('./admin/admin.routes').then((m) => m.adminRoutes) },
];
The app.routes.ts is the routes’s, and the routes’s is the array’s. The two are the pair, and the pair is the pattern’s.
Why the pattern 3 matters. The pattern 3 is the app.routes.ts‘s, and the app.routes.ts‘s is the separation’s. The app.config.ts imports the routes, and the routes is the array’s. The two are the pair, and the pair is the organization’s.
The pattern 4: the environment’s. The environment’s is the environment.ts‘s, and the environment.ts‘s is the configuration’s.
export const environment = {
production: false,
apiUrl: 'http://localhost:3000',
};
The environment’s is the environment.ts‘s, and the environment.ts‘s is the configuration’s. The two are the pair, and the pair is the pattern’s.
Why the pattern 4 matters. The pattern 4 is the environment’s, and the environment’s is the config’s. The environment’s is the build’s, and the build’s is the file’s replacement’s. The two are the pair, and the pair is the design’s.
The pattern 5: the provideX‘s order. The provideX‘s order is the array’s, and the array’s is the declaration’s.
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor])),
provideAnimations(),
]
The provideX‘s order is the array’s, and the array’s is the declaration’s. The two are the pair, and the pair is the pattern’s.
Why the pattern 5 matters. The pattern 5 is the order’s, and the order’s is the design’s. The provideZoneChangeDetection is the first’s, and the provideRouter is the second’s. The two are the pair, and the pair is the dependency’s.
Why the patterns matter. The patterns are the vocabulary’s, and the vocabulary’s is the fluency’s. The five are the common’s, and the common’s is the practice’s. The two are the pair, and the pair is the skill’s.
Complete Example Session
// ============================================
// PART 1: THE MINIMAL'S MAIN.TS
// ============================================
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
// ============================================
// PART 2: THE APP.CONFIG.TS
// ============================================
// app.config.ts
import { ApplicationConfig, provideZoneChangeDetection, ErrorHandler } from '@angular/core';
import { provideRouter, withComponentInputBinding, withViewTransitions } from '@angular/router';
import { provideHttpClient, withInterceptors, withFetch } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { routes } from './app.routes';
import { authInterceptor } from './interceptors/auth.interceptor';
import { GlobalErrorHandler } from './handlers/global-error.handler';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(
routes,
withComponentInputBinding(),
withViewTransitions(),
),
provideHttpClient(
withFetch(),
withInterceptors([authInterceptor]),
),
provideAnimations(),
{ provide: ErrorHandler, useClass: GlobalErrorHandler },
],
};
// ============================================
// PART 3: THE APP.ROUTES.TS
// ============================================
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: '',
loadComponent: () => import('./home/home.component').then((m) => m.HomeComponent),
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes').then((m) => m.adminRoutes),
},
{
path: '**',
loadComponent: () => import('./not-found/not-found.component').then((m) => m.NotFoundComponent),
},
];
// ============================================
// PART 4: THE APP.COMPONENT.TS
// ============================================
// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink, RouterLinkActive],
template: `
<nav>
<a routerLink="/" routerLinkActive="active">Home</a>
<a routerLink="/admin" routerLinkActive="active">Admin</a>
</nav>
<router-outlet />
`,
})
export class AppComponent {}
// ============================================
// PART 5: THE ENVIRONMENT'S
// ============================================
// environments/environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:3000',
};
// environments/environment.prod.ts
export const environment = {
production: true,
apiUrl: 'https://api.example.com',
};
// ============================================
// PART 6: THE ENABLE PROD MODE
// ============================================
// main.ts
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
// ============================================
// PART 7: THE ZONELESS'S
// ============================================
// app.config.ts
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
provideRouter(routes),
provideHttpClient(),
],
};
// ============================================
// PART 8: THE ANGULAR.JSON'S
// ============================================
// angular.json
{
"projects": {
"app": {
"architect": {
"build": {
"options": {
"browser": "src/main.ts",
"polyfills": ["zone.js"], // remove for the zoneless
"tsConfig": "tsconfig.app.json"
}
}
}
}
}
}
// ============================================
// PART 9: THE BOOTSTRAP'S ERROR
// ============================================
bootstrapApplication(AppComponent, appConfig)
.catch((err) => {
console.error('The bootstrap failed:', err);
// The custom's handling
});
// ============================================
// PART 10: WHAT NOT TO DO
// ============================================
// Don't use the NgModule's bootstrap with the standalone
// platformBrowserDynamic().bootstrapModule(AppModule); // ⚠️
// Don't forget the provideRouter
providers: [] // the router's missing // ⚠️
// Don't forget the provideHttpClient
providers: [] // the HTTP's missing // ⚠️
// Don't use the zoneless without the test
// The experimental's. // ⚠️
// Don't forget the enableProdMode
// The development's checks's. // ⚠️
// Don't mix the provideX and the class's provider
// The two are the different, and the different is the confusion.│// ⚠️
The ten parts cover the minimal’s main.ts, the app.config.ts, the app.routes.ts, the app.component.ts, the environment’s, the enableProdMode, the zoneless’s, the angular.json‘s, the bootstrap’s error, and the anti-patterns.
Quick Reference
The Bootstrap
| The item | The purpose |
|---|---|
The bootstrapApplication | The root’s |
The ApplicationConfig | The providers’s |
The app.config.ts | The config’s file |
The app.routes.ts | The routes’s file |
The main.ts | The entry’s file |
The provideX‘s Functions
| The function | The purpose |
|---|---|
The provideRouter | The router |
The provideHttpClient | The HTTP |
The provideAnimations | The animations |
The provideZoneChangeDetection | The zone |
The provideExperimentalZonelessChangeDetection | The zoneless |
The provideRouter‘s Features
| The feature | The purpose |
|---|---|
The withComponentInputBinding | The input’s |
The withViewTransitions | The transition’s |
The withInMemoryScrolling | The scroll’s |
The withRouterConfig | The config’s |
The withDebugTracing | The debug’s |
The provideHttpClient‘s Features
| The feature | The purpose |
|---|---|
The withFetch | The fetch’s |
The withInterceptors | The interceptor’s |
The withRequestsMadeViaParent | The parent’s |
The Bootstrap’s Options
| The option | The purpose |
|---|---|
The ErrorHandler | The global’s |
The ApplicationRef | The inject’s |
The enableProdMode | The production’s |
The Zoneless’s
| The item | The purpose |
|---|---|
The provideExperimentalZonelessChangeDetection | The zoneless’s |
The zone.js‘s removal | The polyfills’s |
| The signals’s | The requirement’s |
Best Practices
✅ Do This:
// Use the bootstrapApplication
bootstrapApplication(AppComponent, appConfig); // ✅
// Use the app.config.ts
export const appConfig: ApplicationConfig = { providers: [...] }; // ✅
// Use the provideRouter
provideRouter(routes, withComponentInputBinding()) // ✅
// Use the provideHttpClient with the features
provideHttpClient(withFetch(), withInterceptors([authInterceptor])) // ✅
// Use the provideZoneChangeDetection with the coalescing
provideZoneChangeDetection({ eventCoalescing: true }) // ✅
// Use the lazy's routes
{ path: 'admin', loadChildren: () => import('./admin/admin.routes') } // ✅
// Use the ErrorHandler's replacement
{ provide: ErrorHandler, useClass: GlobalErrorHandler } // ✅
// Use the enableProdMode
if (environment.production) enableProdMode(); // ✅
❌ Don’t Do This:
// Don't use the NgModule's bootstrap with the standalone
platformBrowserDynamic().bootstrapModule(AppModule) // ⚠️
// Don't forget the provideRouter
providers: [] // the router's missing // ⚠️
// Don't forget the provideHttpClient
providers: [] // the HTTP's missing // ⚠️
// Don't use the zoneless without the test
provideExperimentalZonelessChangeDetection() // the risk // ⚠️
// Don't forget the enableProdMode
// The development's checks's. // ⚠️
// Don't mix the provideX and the class's provider
// The two are the different. // ⚠️
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| The NgModule’s bootstrap | The standalone’s error | The bootstrapApplication |
The missing provideRouter | The route’s error | The provideRouter |
The missing provideHttpClient | The HTTP’s error | The provideHttpClient |
The withFetch‘s missing | The legacy’s | The withFetch() |
The provideAnimations‘s missing | The Material’s error | The provideAnimations() |
The ErrorHandler‘s missing | The unhandled’s | The provider |
The enableProdMode‘s missing | The slow | The enableProdMode() |
| The zoneless’s premature | The experimental’s | The careful |
Real-World Examples
1. The minimal’s main.ts
bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
2. The app.config.ts
export const appConfig: ApplicationConfig = { providers: [...] };
3. The provideRouter
provideRouter(routes, withComponentInputBinding())
4. The provideHttpClient
provideHttpClient(withFetch(), withInterceptors([authInterceptor]))
5. The provideAnimations
provideAnimations()
6. The provideZoneChangeDetection
provideZoneChangeDetection({ eventCoalescing: true })
7. The lazy’s routes
{ path: 'admin', loadChildren: () => import('./admin/admin.routes') }
8. The ErrorHandler
{ provide: ErrorHandler, useClass: GlobalErrorHandler }
9. The environment’s
export const environment = { production: false, apiUrl: '...' };
10. The enableProdMode
if (environment.production) enableProdMode();
Visual: The Bootstrap’s Flow
┌──────────────────────────────────────────────────────────┐
│ THE main.ts │
│ │ │
│ │ 1. The import of the AppComponent │
│ │ 2. The import of the appConfig │
│ │ 3. The bootstrapApplication(AppComponent, appConfig)│
│ │ 4. The catch (the error's) │
│ ▼ │
│ THE appConfig │
│ │ │
│ ├── The provideZoneChangeDetection │
│ ├── The provideRouter │
│ ├── The provideHttpClient │
│ └── The provideAnimations │
│ │
│ The bootstrapApplication is the entry's, and the │
│ appConfig is the providers's. │
│ │
└──────────────────────────────────────────────────────────┘
Visual: The provideRouter’s Features
┌──────────────────────────────────────────────────────────┐
│ THE provideRouter(routes) │
│ │ │
│ ├── The withComponentInputBinding │
│ │ The route's params → the component's inputs │
│ │ │
│ ├── The withViewTransitions │
│ │ The view's transition's │
│ │ │
│ ├── The withInMemoryScrolling │
│ │ The scroll's restoration's │
│ │ │
│ └── The withDebugTracing │
│ The debug's │
│ │
│ The features are the composable's, and the composable's │
│ is the modern's. │
│ │
└──────────────────────────────────────────────────────────┘
Visual: The provideHttpClient’s Features
┌──────────────────────────────────────────────────────────┐
│ THE provideHttpClient() │
│ │ │
│ ├── The withFetch │
│ │ The fetch's API's │
│ │ │
│ ├── The withInterceptors([...]) │
│ │ The functional's interceptors │
│ │ │
│ └── The withRequestsMadeViaParent │
│ The parent's injector's │
│ │
│ The features are the composable's. │
│ │
└──────────────────────────────────────────────────────────┘
Visual: The Bootstrap’s Structure
┌──────────────────────────────────────────────────────────┐
│ src/ │
│ main.ts ← the bootstrap's │
│ app/ │
│ app.component.ts ← the root's │
│ app.config.ts ← the providers's │
│ app.routes.ts ← the routes's │
│ interceptors/ ← the interceptors's │
│ handlers/ ← the error's handlers's │
│ │
│ The main.ts is the three's, and the app.config.ts is │
│ the providers's. │
│ │
└──────────────────────────────────────────────────────────┘
Visual: The Zoneless’s
┌──────────────────────────────────────────────────────────┐
│ THE ZONE'S (the default) │
│ The zone.js's polyfill's │
│ The provideZoneChangeDetection │
│ The automatic's │
│ │
├──────────────────────────────────────────────────────────┤
│ THE ZONELESS'S (the experimental) │
│ The zone.js's removal's │
│ The provideExperimentalZonelessChangeDetection │
│ The signals's │
│ │
│ The zoneless's is the future's, and the future's is the │
│ performance's. │
│ │
└──────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
The bootstrapApplication | The root’s |
The ApplicationConfig | The providers’s |
The provideRouter | The router’s |
The provideHttpClient | The HTTP’s |
The provideAnimations | The animations’s |
The provideZoneChangeDetection | The zone’s |
The provideExperimentalZonelessChangeDetection | The zoneless’s |
The withComponentInputBinding | The router’s feature |
The withFetch | The HTTP’s feature |
The withInterceptors | The HTTP’s feature |
Key takeaways:
- The
bootstrapApplication(AppComponent, appConfig)bootstraps the root component directly — the root module is not needed, and theApplicationConfigholds the providers - The
app.config.tsholds the providers, and themain.tsholds the bootstrap — the two are the separate, and the separate is the organization’s - The
provideRouter(routes)provides the router, and theroutesarray is the application’s — thewithComponentInputBindingand thewithViewTransitionsare the features - The
provideHttpClient()provides the HTTP, and thewithInterceptorsand thewithFetchare the features — the functional’s interceptors are the modern’s - The
provideAnimations()provides the animations, and the Angular Material requires it — theprovideNoopAnimationsis the test’s - The
provideZoneChangeDetection({ eventCoalescing: true })is the zone’s — theeventCoalescingis the performance’s - The
provideExperimentalZonelessChangeDetection()is the zoneless’s — it is the experimental’s, and the signals’s is the requirement’s - The
ErrorHandler‘s replacement is the provider’s — the{ provide: ErrorHandler, useClass: GlobalErrorHandler }is the global’s - The
enableProdMode()is the production’s — the development’s checks’s is the verbose’s, and the production’s is the fast’s - The
bootstrapApplication‘scatchhandles the error — theconsole.erroris the common’s, and the custom’s is the advanced’s
Remember: The bootstrap without the NgModule is the bootstrapApplication‘s, and the ApplicationConfig‘s is the providers’s. The provideX‘s is the concise’s, and the features’s is the composable’s. The app.config.ts is the providers’s, and the main.ts is the entry’s. The zoneless’s is the future’s, and the signals’s is the modern’s. The bootstrap’s is the start’s, and the start’s is the design’s.
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!