angular.courses

Provider Configuration Evolution

From NgModule providers to bootstrapApplication providers, provideRouter, provideHttpClient, route providers, and importProvidersFrom

Overview

Angular provider configuration has evolved from NgModule-based providers (providers array in @NgModule) to standalone provider APIs: bootstrapApplication with a providers array, provideRouter, provideHttpClient, route-level providers, and importProvidersFrom to pull providers from an NgModule. This practice summarizes the evolution and how to configure providers in modern Angular.

The Evolution

v2NgModule providers

Providers declared in @NgModule({ providers: [...] }); imported via imports array.

v14bootstrapApplication + providers

Root providers in bootstrapApplication(Component, { providers: [...] }); provideRouter, provideHttpClient, etc.

Code Comparison

Root providers: NgModule vs bootstrapApplication

NgModule providersBefore v14
app.module.ts
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, RouterModule.forRoot(routes), HttpClientModule],
providers: [
UserService,
{ provide: API_URL, useValue: 'https://api.example.com' }
],
bootstrap: [AppComponent]
})
export class AppModule {}
bootstrapApplication providersv14+
main.ts
bootstrapApplication(AppComponent, {
providers: [
UserService,
{ provide: API_URL, useValue: 'https://api.example.com' },
provideRouter(routes),
provideHttpClient()
]
});

Router and HttpClient

RouterModule / HttpClientModuleBefore v14
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot(routes),
HttpClientModule
],
...
})
export class AppModule {}
provideRouter / provideHttpClientv14.2+
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes),
provideHttpClient()
]
});

Using NgModule providers in standalone app: importProvidersFrom

Import NgModule for providersBefore v14
@NgModule({
imports: [SomeFeatureModule], // brings in providers from module
...
})
export class AppModule {}
importProvidersFromv14+
import { importProvidersFrom } from '@angular/core';
bootstrapApplication(AppComponent, {
providers: [
importProvidersFrom(SomeFeatureModule)
]
});

Route-level providers

Lazy module with providersBefore v14
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
}
// AdminModule has providers: [AdminService]
Route providersv14+
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes').then(m => m.adminRoutes),
providers: [AdminService]
}
// Or in child route config

providedIn: 'root' (unchanged)

Tree-shakeable serviceBefore v14
@Injectable({ providedIn: 'root' })
export class UserService {}
Same with standalonev14+
@Injectable({ providedIn: 'root' })
export class UserService {}
// No change; works with bootstrapApplication

Key Differences

Root config:NgModule providers + importsbootstrapApplication({ providers })
Router/HTTP:RouterModule.forRoot, HttpClientModuleprovideRouter(), provideHttpClient()
NgModule reuse:imports: [SomeModule]importProvidersFrom(SomeModule)
Route scope:Lazy module providersRoute providers array

Benefits

Standalone

No NgModule required for root config

Tree-shaking

Provider functions are analyzable

Composition

Mix provideRouter, provideHttpClient, custom providers

Route scope

Providers per route for lazy scope

Migration

importProvidersFrom for existing modules

Provider configuration today

  • Root: Use bootstrapApplication(AppComponent, { providers: [...] }). Add provideRouter(routes), provideHttpClient(), and app-wide services.
  • Route: Use providers: [MyService] on a route so the service is available only for that route (and children) and is destroyed when leaving the route.
  • NgModule reuse: Use importProvidersFrom(MyModule) in the root or route providers array to pull in that module's providers without importing the module for declarations.
  • Tree-shakeable: Keep @Injectable({ providedIn: 'root' }) for services used app-wide; they are tree-shaken if unused.

Route providers and lazy loading

Route-level providers are created when the route is activated and destroyed when it is left. Use them for feature-specific services that should not live in the root injector.