Standalone components vs NgModules
When to use standalone components and when NgModules still make sense; comparison and coexistence
Overview
Angular supports both standalone components and NgModules. New applications and components should prefer standalone; NgModules remain for lazy-loaded feature modules, shared modules in legacy code, and some library packaging. This practice compares the two and explains when to use each and how they coexist.
The Evolution
All components, directives, and pipes had to be declared in an NgModule.
Standalone components and directives introduced as opt-in.
Standalone recommended for new code; NgModules still used where needed.
Comparison
Bootstrapping
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);import { bootstrapApplication } from '@angular/platform-browser';import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent);Declaring dependencies
@NgModule({ declarations: [MyComponent, MyDirective, MyPipe], imports: [CommonModule, OtherFeatureModule], exports: [MyComponent],})export class MyModule {}// Component does not declare its own dependencies@Component({ selector: 'app-my', standalone: true, imports: [CommonModule, OtherStandaloneComponent], template: `...`,})export class MyComponent {}// Component declares exactly what it usesLazy loading
const routes: Routes = [ { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule), },];// AdminModule declares and exports its componentsconst routes: Routes = [ { path: 'admin', loadChildren: () => import('./admin/admin.routes').then(m => m.adminRoutes), },];// admin.routes.ts exports Routes with standalone componentsShared pieces: SharedModule vs standalone imports
@NgModule({ declarations: [SharedPipe, SharedDirective], exports: [SharedPipe, SharedDirective],})export class SharedModule {}
// Consumer module@NgModule({ imports: [SharedModule], declarations: [FeatureComponent],})export class FeatureModule {}// Shared pipe/directive are standalone@Component({ standalone: true, imports: [SharedPipe, SharedDirective], template: `...`,})export class FeatureComponent {}Key Differences
Benefits
No separate NgModule file for most features
Only imported code is bundled
Dependencies visible on the component
Standalone and NgModules work together
Gradual move from modules to standalone
When to use standalone
- New apps and components: Use
standalone: trueandbootstrapApplication. - New lazy routes: Use
loadChildrenreturning routes orloadComponentfor a single component. - Libraries: Publish standalone APIs; consumers import only what they use.
When NgModules still make sense
- Existing code: Keep NgModules during gradual migration.
- Lazy-loaded feature modules: You can keep a feature as an NgModule and lazy-load it until you refactor to standalone routes.
- Some third-party libraries: May still expose an NgModule; import it in your standalone component’s
importsor inimportProvidersFrom()when bootstrapping.
Mixing both
You can use importProvidersFrom(SomeModule) in bootstrapApplication(AppComponent, { providers: [...], }) to pull providers from an NgModule into a standalone app. Standalone components can also list NgModules in their imports array.
Default in Angular 19+
New components are generated with standalone: true by default. Use NgModules only where you have a concrete reason (e.g. legacy module, library contract).
Coexistence
| Scenario | Approach | |----------|----------| | New feature | Standalone component + imports | | Legacy feature module | Keep NgModule; lazy-load it; migrate when convenient | | Shared directives/pipes | Prefer standalone; add to each component’s imports | | App bootstrap | bootstrapApplication with root standalone component | | Providers from module | importProvidersFrom(MyModule) in bootstrap or route config |
Related Evolutions
Standalone components vs NgModules
When to use standalone components and when NgModules still make sense; comparison and coexistence
Overview
Angular supports both standalone components and NgModules. New applications and components should prefer standalone; NgModules remain for lazy-loaded feature modules, shared modules in legacy code, and some library packaging. This practice compares the two and explains when to use each and how they coexist.
The Evolution
All components, directives, and pipes had to be declared in an NgModule.
Standalone components and directives introduced as opt-in.
Standalone recommended for new code; NgModules still used where needed.
Comparison
Bootstrapping
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);import { bootstrapApplication } from '@angular/platform-browser';import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent);Declaring dependencies
@NgModule({ declarations: [MyComponent, MyDirective, MyPipe], imports: [CommonModule, OtherFeatureModule], exports: [MyComponent],})export class MyModule {}// Component does not declare its own dependencies@Component({ selector: 'app-my', standalone: true, imports: [CommonModule, OtherStandaloneComponent], template: `...`,})export class MyComponent {}// Component declares exactly what it usesLazy loading
const routes: Routes = [ { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule), },];// AdminModule declares and exports its componentsconst routes: Routes = [ { path: 'admin', loadChildren: () => import('./admin/admin.routes').then(m => m.adminRoutes), },];// admin.routes.ts exports Routes with standalone componentsShared pieces: SharedModule vs standalone imports
@NgModule({ declarations: [SharedPipe, SharedDirective], exports: [SharedPipe, SharedDirective],})export class SharedModule {}
// Consumer module@NgModule({ imports: [SharedModule], declarations: [FeatureComponent],})export class FeatureModule {}// Shared pipe/directive are standalone@Component({ standalone: true, imports: [SharedPipe, SharedDirective], template: `...`,})export class FeatureComponent {}Key Differences
Benefits
No separate NgModule file for most features
Only imported code is bundled
Dependencies visible on the component
Standalone and NgModules work together
Gradual move from modules to standalone
When to use standalone
- New apps and components: Use
standalone: trueandbootstrapApplication. - New lazy routes: Use
loadChildrenreturning routes orloadComponentfor a single component. - Libraries: Publish standalone APIs; consumers import only what they use.
When NgModules still make sense
- Existing code: Keep NgModules during gradual migration.
- Lazy-loaded feature modules: You can keep a feature as an NgModule and lazy-load it until you refactor to standalone routes.
- Some third-party libraries: May still expose an NgModule; import it in your standalone component’s
importsor inimportProvidersFrom()when bootstrapping.
Mixing both
You can use importProvidersFrom(SomeModule) in bootstrapApplication(AppComponent, { providers: [...], }) to pull providers from an NgModule into a standalone app. Standalone components can also list NgModules in their imports array.
Default in Angular 19+
New components are generated with standalone: true by default. Use NgModules only where you have a concrete reason (e.g. legacy module, library contract).
Coexistence
| Scenario | Approach | |----------|----------| | New feature | Standalone component + imports | | Legacy feature module | Keep NgModule; lazy-load it; migrate when convenient | | Shared directives/pipes | Prefer standalone; add to each component’s imports | | App bootstrap | bootstrapApplication with root standalone component | | Providers from module | importProvidersFrom(MyModule) in bootstrap or route config |