Angular 59 🅰️ Standalone Components
Standalone components are Angular’s answer to the boilerplate that NgModules required for every application. Before them, every component had to be declared in an NgModule, every directive had to be exported from a shared module, and every lazy-loaded route needed its own module. The standalone flag flips the model: a component declares its own dependencies directly in its imports array, and the NgModule becomes optional. The result is a smaller mental model, more explicit dependencies, and better tree-shaking. Since Angular 19, standalone: true is the default — a component is standalone unless explicitly marked otherwise. This chapter covers what standalone components are, how they replace NgModules, the bootstrapApplication API, the imports array, the lazy loading with loadComponent and loadChildren, the dependency injection without modules, the migration path, and the patterns that make standalone applications maintainable.
Key point: A standalone component declares its own dependencies in the imports array of the @Component decorator. It does not need to be declared in an NgModule, and it can be imported directly by any other standalone component or NgModule . The bootstrapApplication function replaces the bootstrapModule API, and it bootstraps the root component directly without a root module . The provideRouter, provideHttpClient, and the other provideX functions configure the application’s providers in the ApplicationConfig . The loadComponent function lazy-loads a standalone component, and the loadChildren function lazy-loads a set of routes . The migration schematic converts an existing NgModule-based application to standalone incrementally, and the process is safe and non-breaking .
Why standalone components
The NgModule was Angular’s original organizational unit. Every component belonged to exactly one NgModule, and the NgModule declared what the component could use. The model worked, but it had costs.
The boilerplate. A new component required a new NgModule declaration. A shared directive required an NgModule that declared and exported it. A lazy route required its own module. The boilerplate grew with the application, and the indirection made the dependencies harder to trace .
The indirectness. A component’s dependencies were declared in the NgModule, not the component. Reading the component’s template and finding a pipe required finding the NgModule and checking its imports. The dependency was implicit, and the implicit was the debugging’s cost .
The learning curve. NgModules were the first concept a new Angular developer learned, and they were not intuitive. The declarations, the imports, the exports, and the providers each had their own rules, and the rules were easy to confuse .
The standalone’s answer. The standalone component declares its own dependencies. The imports array is the list, and the list is the explicit. The component is self-contained, and the self-contained is the simpler .
Why the standalone is the default. Since Angular 19, the standalone: true is the default. A new component is standalone unless explicitly marked otherwise. The NgModule remains for backward compatibility, and the two coexist .
Why the standalone matters for the tree-shaking. The standalone component’s dependencies are explicit, and the bundler can tree-shake the unused. The NgModule’s dependencies are the module’s, and the module’s is the coarser. The standalone’s is the finer, and the finer is the optimization .
Why the standalone matters for the lazy loading. The loadComponent lazy-loads a single standalone component, and the loadChildren lazy-loads a set of routes. The two are the modern, and the modern is the simpler. The NgModule’s lazy loading required the module, and the module is the legacy .
The standalone component
A standalone component is a component with the standalone: true flag (or, since Angular 19, the default). Its dependencies are declared in the imports array.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-user-profile',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<div class="profile">
<p>{{ user.name }}</p>
<p>{{ user.email }}</p>
</div>
`,
})
export class UserProfileComponent {
user = { name: 'John Doe', email: 'john@example.com' };
}
The standalone: true is the flag, and the imports: [CommonModule, FormsModule] is the list. The component is self-contained, and the self-contained is the explicit .
Why the imports array matters. The imports array is the component’s dependencies, and the dependencies are the explicit. The CommonModule provides the *ngIf and the *ngFor, and the FormsModule provides the ngModel. The two are the list, and the list is the component’s .
Why the standalone component can import the NgModule. The standalone component can import the NgModule, and the NgModule’s exports are the available. The two are the compatible, and the compatible is the migration’s .
Why the standalone component can be imported by the NgModule. The NgModule’s imports array can include the standalone component, and the component’s exports are the available. The two are the compatible, and the compatible is the migration’s .
Why the standalone component can be imported by another standalone component. The standalone component’s imports array can include the other standalone component, and the other is the available. The two are the compatible, and the compatible is the direct .
Why the standalone component’s template can use the other’s selector. The other component’s selector is the tag, and the tag is the available. The two are the pair, and the pair is the composition.
Why the standalone component matters for the new project. The new project’s ng new creates the standalone components, and the standalone is the default. The NgModule is the legacy, and the legacy is the migration’s .
The bootstrapApplication
The bootstrapApplication function bootstraps the root component directly, without the root NgModule. It replaces the bootstrapModule API.
// 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) bootstraps the AppComponent, and the appConfig is the configuration. The root module is not needed, and the not needed is the simpler .
Why the bootstrapApplication matters. The bootstrapApplication is the modern, and the modern is the direct. The bootstrapModule is the legacy, and the legacy is the module’s. The two are the equivalent, and the bootstrapApplication is the preference .
The ApplicationConfig. The ApplicationConfig is the object with the providers array, and the providers are the application’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 provideZoneChangeDetection, the provideRouter, and the provideHttpClient are the providers. The appConfig is the object, and the object is the configuration .
Why the provideX functions matter. The provideX is the modern, and the modern is the concise. The provideRouter(routes) is the router’s, and the provideHttpClient() is the HTTP’s. The two are the providers, and the providers are the functions .
Why the ApplicationConfig matters. The ApplicationConfig is the configuration, and the configuration is the bootstrapApplication‘s second argument. The root module’s providers is the legacy, and the ApplicationConfig‘s is the modern. The two are the equivalent, and the ApplicationConfig is the modern.
Why the bootstrapApplication should be the standard. The bootstrapApplication is the standard for the standalone, and the standalone is the modern. The bootstrapModule is the legacy, and the legacy is the migration’s. The two are the choice, and the bootstrapApplication is the preference .
The routing without the NgModule
The routing without the NgModule uses the provideRouter and the loadComponent and the loadChildren. The three are the modern, and the modern is the standalone’s.
The provideRouter. The provideRouter(routes) provides the routes, and the routes are the array.
// app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
export const routes: Routes = [
{ path: '', component: HomeComponent },
{
path: 'admin',
loadComponent: () => import('./admin/admin.component').then((m) => m.AdminComponent),
},
];
The provideRouter(routes) is the provider, and the routes is the array. The loadComponent is the lazy, and the lazy is the component’s .
Why the provideRouter matters. The provideRouter is the modern, and the modern is the function. The RouterModule.forRoot is the legacy, and the legacy is the module’s. The two are the equivalent, and the provideRouter is the modern.
The loadComponent. The loadComponent lazy-loads a single standalone component.
{
path: 'admin',
loadComponent: () => import('./admin/admin.component').then((m) => m.AdminComponent),
}
The loadComponent is the function, and the function returns the Promise. The import is the dynamic, and the dynamic is the lazy. The two are the pair, and the pair is the lazy .
Why the loadComponent matters. The loadComponent is the lazy, and the lazy is the component’s. The NgModule’s lazy loading required the module, and the module is the legacy. The loadComponent is the modern, and the modern is the simpler .
The loadChildren. The loadChildren lazy-loads a set of routes.
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes').then((m) => m.adminRoutes),
}
The loadChildren is the function, and the function returns the Promise. The adminRoutes is the array, and the array is the routes’. The two are the pair, and the pair is the lazy .
Why the loadChildren matters. The loadChildren is the lazy, and the lazy is the routes’. The route’s children is the nested, and the nested is the feature’s. The two are the pair, and the pair is the encapsulation .
Why the loadChildren should be the feature’s. The loadChildren should be the feature’s, and the feature’s is the encapsulated. The feature’s routes are the feature’s, and the feature’s is the natural. The two are the pair, and the pair is the design .
Why the loadComponent and the loadChildren can be the together. The two can be the together, and the together is the composition. The loadComponent is the single, and the loadChildren is the set. The two are the pair, and the pair is the pattern.
The dependency injection without the NgModule
The dependency injection without the NgModule uses the provideX functions, the providedIn: 'root', and the component’s providers array. The three are the modern, and the modern is the standalone’s.
The providedIn: 'root'. The providedIn: 'root' makes the service the application’s singleton.
@Injectable({ providedIn: 'root' })
export class UserService {
private readonly users = signal<User[]>([]);
}
The UserService is the root’s, and the instance is the singleton. The state is the application’s, and the application’s is the shared .
Why the providedIn: 'root' matters. The providedIn: 'root' is the tree-shakable, and the tree-shakable is the optimization. The NgModule’s providers is the legacy, and the legacy is the module’s. The two are the equivalent, and the providedIn: 'root' is the modern .
The component’s providers. The component’s providers array provides the service at the component’s scope.
@Component({
selector: 'app-feature',
standalone: true,
providers: [FeatureService],
template: `<app-child />`,
})
export class FeatureComponent {}
The FeatureService is the component’s, and the instance is the component’s. The state is the component’s, and the component’s is the local .
Why the component’s providers matters. The component’s providers is the local, and the local is the scope. The NgModule’s providers is the legacy, and the legacy is the module’s. The two are the equivalent, and the component’s is the modern .
The route’s providers. The route’s providers array provides the service at the route’s scope.
const routes: Routes = [
{
path: 'admin',
providers: [AdminService],
children: [{ path: '', component: AdminComponent }],
},
];
The AdminService is the route’s, and the instance is the route’s. The state is the route’s, and the route’s is the feature’s .
Why the route’s providers matters. The route’s providers is the feature’s, and the feature’s is the scope. The lazy’s is the cleanup, and the cleanup is the route’s. The two are the pair, and the pair is the scope.
Why the injection is the same without the NgModule. The injection is the same, and the same is the inject() and the constructor. The NgModule is not needed, and the not needed is the simpler. The two are the pair, and the pair is the modern .
Why the standalone’s injection matters. The standalone’s injection is the modern, and the modern is the explicit. The providedIn: 'root' is the application’s, and the component’s providers is the local. The two are the pair, and the pair is the pattern.
The migration from the NgModule
The migration from the NgModule is the incremental, and the incremental is the safe. The schematic converts the components, the directives, and the pipes to the standalone, and the process has the three steps .
The step 1: convert the declarations to standalone. The schematic removes the standalone: false and adds the dependencies to the imports array.
ng generate @angular/core:standalone
# Select "Convert all components, directives and pipes to standalone"
The schematic converts the declarations, and the declarations are the components, the directives, and the pipes. The imports array is the list, and the list is the dependencies .
Why the step 1 matters. The step 1 is the conversion, and the conversion is the components’. The imports array is the explicit, and the explicit is the standalone’s. The two are the pair, and the pair is the migration .
The step 2: remove the unnecessary NgModules. The schematic removes the NgModules that have no declarations, no providers, and no bootstrap components.
ng generate @angular/core:standalone
# Select "Remove unnecessary NgModule classes"
The schematic removes the modules, and the modules are the unnecessary. The TODO comments are the manual’s, and the manual is the edge’s .
Why the step 2 matters. The step 2 is the cleanup, and the cleanup is the modules’. The NgModules are the legacy, and the legacy is the removal’s. The two are the pair, and the pair is the migration .
The step 3: switch to the standalone bootstrapping. The schematic converts the bootstrapModule to the bootstrapApplication and removes the root NgModule.
ng generate @angular/core:standalone
# Select "Bootstrap the project using standalone APIs"
The schematic converts the bootstrapping, and the bootstrapping is the bootstrapApplication‘s. The root module is removed, and the removed is the simpler .
Why the step 3 matters. The step 3 is the bootstrapping, and the bootstrapping is the root’s. The bootstrapApplication is the modern, and the modern is the standalone’s. The two are the pair, and the pair is the migration .
Why the migration is the incremental. The migration is the incremental, and the incremental is the safe. The three steps are the sequence, and the sequence is the order. The two are the pair, and the pair is the migration .
Why the migration’s manual fixes matter. The migration’s manual fixes are the edge’s, and the edge’s is the TODO. The schematic’s automatic is the most, and the most is the majority. The two are the pair, and the pair is the migration .
The best practices
The standalone’s best practices are the explicit, and the explicit is the dependencies’. The seven are the common, and the common is the pattern.
The organize by feature. The feature’s folder is the components’, and the components’ is the feature’s. The feature’s is the natural, and the natural is the organization .
The barrel files. The index.ts is the exports’, and the exports’ is the imports’. The barrel is the simplification, and the simplification is the convenience .
The minimize the imports. The imports array should have only the needed, and the needed is the bundle’s. The unused is the bundle’s cost, and the cost is the optimization .
The document the dependencies. The imports array should be the documented, and the documented is the why. The comment is the why, and the why is the clarity .
The leverage the CLI. The CLI’s migration is the schematic’s, and the schematic’s is the consistency. The CLI’s is the modern, and the modern is the preference .
The test in isolation. The standalone’s test is the explicit, and the explicit is the imports’. The test’s is the focused, and the focused is the fast .
The bundle analysis. The ng build --stats-json is the analysis, and the analysis is the optimization. The bundle’s is the size, and the size is the performance .
Why the best practices matter. The best practices are the pattern, and the pattern is the discipline. The seven are the common, and the common is the value. The two are the pair, and the pair is the practice.
Why the standalone’s pitfalls matter. The missing imports are the pitfall, and the pitfall is the runtime’s. The legacy libraries are the check’s, and the check is the compatibility. The overusing the providers is the scope’s, and the scope is the singleton’s .
Why the standalone’s performance matters. The standalone’s performance is the tree-shaking, and the tree-shaking is the bundle’s. The lazy loading is the chunk’s, and the chunk is the startup’s. The two are the pair, and the pair is the performance .
Complete Example Session
// ============================================
// PART 1: THE STANDALONE COMPONENT
// ============================================
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-user-profile',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<div class="profile">
<p>{{ user.name }}</p>
<p>{{ user.email }}</p>
</div>
`,
})
export class UserProfileComponent {
user = { name: 'John Doe', email: 'john@example.com' };
}
// ============================================
// PART 2: THE BOOTSTRAP
// ============================================
// 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 3: THE APPLICATION CONFIG
// ============================================
// 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(),
],
};
// ============================================
// PART 4: THE ROUTES
// ============================================
// 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),
},
];
// ============================================
// PART 5: THE FEATURE'S ROUTES
// ============================================
// admin.routes.ts
import { Routes } from '@angular/router';
export const adminRoutes: Routes = [
{
path: '',
loadComponent: () => import('./admin.component').then((m) => m.AdminComponent),
},
{
path: 'users',
loadComponent: () => import('./users/users.component').then((m) => m.UsersComponent),
},
];
// ============================================
// PART 6: THE ROOT COMPONENT
// ============================================
// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `<router-outlet />`,
})
export class AppComponent {}
// ============================================
// PART 7: THE ROOT COMPONENT'S IMPORTS
// ============================================
// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, 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 8: THE SERVICE
// ============================================
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class UserService {
private readonly users = signal<User[]>([]);
readonly userList = this.users.asReadonly();
load(): void {
// the load
}
}
// ============================================
// PART 9: THE ROUTE'S PROVIDER
// ============================================
const routes: Routes = [
{
path: 'admin',
providers: [AdminService],
children: [
{ path: '', component: AdminComponent },
],
},
];
// ============================================
// PART 10: WHAT NOT TO DO
// ============================================
// Don't mix the standalone and the NgModule's bootstrap
// The bootstrapApplication and the bootstrapModule. // ⚠️
// Don't forget the imports array
// The component's dependencies. // ⚠️
// Don't use the string token
// The InjectionToken. // ⚠️
// Don't forget the loadComponent's import
// The dynamic import. // ⚠️
// Don't use the NgModule's declarations
// The standalone's imports. // ⚠️
// Don't forget the base href
// The index.html's base href. // ⚠️
// Don't forget the provideRouter
// The ApplicationConfig's providers. // ⚠️
The ten parts cover the standalone component, the bootstrap, the application config, the routes, the feature’s routes, the root component, the root component’s imports, the service, the route’s provider, and the anti-patterns.
Quick Reference
The Standalone’s Flag
| The version | The default |
|---|---|
| Angular 14-18 | The standalone: true |
| Angular 19+ | The default |
The Bootstrap’s Functions
| The function | The purpose |
|---|---|
bootstrapApplication | The root component |
bootstrapModule | The legacy |
The Providers
| The function | The purpose |
|---|---|
provideRouter | The routes |
provideHttpClient | The HTTP |
provideZoneChangeDetection | The zone |
provideAnimations | The animations |
The Lazy Loading
| The function | The purpose |
|---|---|
loadComponent | The single component |
loadChildren | The set of routes |
The Injection
| The pattern | The scope |
|---|---|
providedIn: 'root' | The application |
The component’s providers | The component |
The route’s providers | The route |
The Migration
| The step | The mode |
|---|---|
| 1 | The “Convert all components” |
| 2 | The “Remove unnecessary NgModules” |
| 3 | The “Bootstrap the project using standalone APIs” |
Best Practices
✅ Do This:
// Use the standalone: true (or the default)
@Component({ standalone: true, imports: [CommonModule] }) // ✅
// Use the bootstrapApplication
bootstrapApplication(AppComponent, appConfig); // ✅
// Use the provideRouter
providers: [provideRouter(routes)] // ✅
// Use the loadComponent for the lazy
{ path: 'admin', loadComponent: () => import(...) } // ✅
// Use the loadChildren for the feature's routes
{ path: 'admin', loadChildren: () => import(...) } // ✅
// Use the providedIn: 'root' for the application's service
@Injectable({ providedIn: 'root' }) // ✅
// Use the component's providers for the local
@Component({ providers: [FeatureService] }) // ✅
// Run the migration's three steps
ng generate @angular/core:standalone // ✅
❌ Don’t Do This:
// Don't mix the standalone and the NgModule's bootstrap
bootstrapModule(AppModule) // the legacy // ⚠️
// Don't forget the imports array
@Component({ standalone: true }) // the missing // ⚠️
// Don't use the NgModule's declarations
declarations: [AppComponent] // the standalone's error // ⚠️
// Don't forget the loadComponent's import
{ path: 'admin', component: AdminComponent } // the eager // ⚠️
// Don't forget the base href
// The index.html's base href. // ⚠️
// Don't forget the provideRouter
providers: [] // the missing // ⚠️
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| The mixed bootstrap | The error | The bootstrapApplication |
The missing imports | The unknown element | The add |
The NgModule’s declarations | The error | The imports |
| The eager route | The bundle’s size | The loadComponent |
The missing base href | The route’s fail | The <base href="/"> |
The missing provideRouter | The route’s fail | The provider |
| The service’s scope | The wrong lifetime | The providedIn |
Real-World Examples
1. The standalone component
@Component({ standalone: true, imports: [CommonModule] })
2. The bootstrap
bootstrapApplication(AppComponent, appConfig);
3. The application config
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes)],
};
4. The lazy component
{ path: 'admin', loadComponent: () => import('./admin.component') }
5. The lazy routes
{ path: 'admin', loadChildren: () => import('./admin.routes') }
6. The root component
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `<router-outlet />`,
})
7. The service
@Injectable({ providedIn: 'root' })
export class UserService {}
8. The component’s provider
@Component({ providers: [FeatureService] })
9. The route’s provider
{ path: 'admin', providers: [AdminService] }
10. The migration
ng generate @angular/core:standalone
Visual: The Standalone
┌──────────────────────────────────────────────────────────┐
│ THE CLASSIC (the NgModule) │
│ The AppModule │
│ declarations: [AppComponent, ChildComponent] │
│ imports: [CommonModule, FormsModule] │
│ bootstrap: [AppComponent] │
│ │
│ THE MODERN (the standalone) │
│ The AppComponent │
│ standalone: true │
│ imports: [CommonModule, FormsModule, ChildComponent]│
│ │
│ The NgModule is not needed, and the component is the │
│ self-contained. │
│ │
└──────────────────────────────────────────────────────────┘
Visual: The Bootstrap
┌──────────────────────────────────────────────────────────┐
│ THE CLASSIC │
│ main.ts: │
│ platformBrowserDynamic().bootstrapModule(AppModule) │
│ │
│ THE MODERN │
│ main.ts: │
│ bootstrapApplication(AppComponent, appConfig) │
│ │
│ THE APPLICATION CONFIG │
│ app.config.ts: │
│ export const appConfig: ApplicationConfig = { │
│ providers: [provideRouter(routes), provideHttpClient()]│
│ }; │
│ │
│ The root module is not needed. │
│ │
└──────────────────────────────────────────────────────────┘
Visual: The Lazy Loading
┌──────────────────────────────────────────────────────────┐
│ THE loadComponent │
│ { path: 'admin', loadComponent: () => import('./admin.component') }│
│ │ │
│ └── The single component. │
│ │
│ THE loadChildren │
│ { path: 'admin', loadChildren: () => import('./admin.routes') }│
│ │ │
│ └── The set of routes. │
│ │
│ The two are the lazy, and the lazy is the bundle's │
│ optimization. │
│ │
└──────────────────────────────────────────────────────────┘
Visual: The Migration
┌──────────────────────────────────────────────────────────┐
│ THE STEP 1 │
│ The "Convert all components, directives and pipes to standalone"│
│ The standalone: false → the imports array. │
│ │
│ THE STEP 2 │
│ The "Remove unnecessary NgModule classes" │
│ The empty modules are removed. │
│ │
│ THE STEP 3 │
│ The "Bootstrap the project using standalone APIs" │
│ The bootstrapModule → the bootstrapApplication. │
│ │
│ The three steps are the incremental, and the │
│ incremental is the safe. │
│ │
└──────────────────────────────────────────────────────────┘
Visual: The Injection
┌──────────────────────────────────────────────────────────┐
│ THE APPLICATION'S │
│ @Injectable({ providedIn: 'root' }) │
│ The singleton. │
│ │
│ THE COMPONENT'S │
│ @Component({ providers: [FeatureService] }) │
│ The per-instance. │
│ │
│ THE ROUTE'S │
│ { path: 'admin', providers: [AdminService] } │
│ The feature's. │
│ │
│ The three are the scopes, and the scopes are the │
│ lifetimes. │
│ │
└──────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| The standalone’s flag | The standalone: true (the default in 19+) |
| The bootstrap | The bootstrapApplication |
| The config | The ApplicationConfig |
| The router’s provider | The provideRouter |
| The lazy’s component | The loadComponent |
| The lazy’s routes | The loadChildren |
| The application’s service | The providedIn: 'root' |
| The component’s service | The @Component({ providers }) |
| The migration | The three steps |
| The base href | The <base href="/"> |
Key takeaways:
- A standalone component declares its own dependencies in the
importsarray, and it does not need an NgModule - The
standalone: trueis the default since Angular 19 — the new components are standalone unless explicitly marked otherwise - The
bootstrapApplicationreplaces thebootstrapModule— the root component is bootstrapped directly, and the root module is not needed - The
ApplicationConfigholds the providers — theprovideRouter, theprovideHttpClient, and the otherprovideXfunctions - The
loadComponentlazy-loads a single component, and theloadChildrenlazy-loads a set of routes - The dependency injection works without the NgModule — the
providedIn: 'root', the component’sproviders, and the route’sproviders - The migration is the incremental and the safe — the three steps are the “Convert all components”, the “Remove unnecessary NgModules”, and the “Bootstrap the project using standalone APIs”
- The standalone improves the tree-shaking and the lazy loading — the explicit dependencies are the finer, and the finer is the optimization
- The best practices are the organize by feature, the barrel files, the minimize the imports, and the test in isolation
- The migration’s manual fixes are the edge’s — the
TODOcomments are the manual’s, and the manual is the developer’s
Remember: The standalone components are the modern Angular, and the standalone: true is the default. The imports array is the component’s dependencies, and the bootstrapApplication is the bootstrap. The provideRouter and the provideHttpClient are the providers, and the loadComponent and the loadChildren are the lazy. The migration is the incremental, and the incremental is the safe. The NgModule is the legacy, and the legacy is the migration’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!