Using Angular Standalone Components to Reduce NgModule Overhead
Learn how Angular standalone components eliminate NgModule boilerplate, simplify bootstrapping, and improve tree‑shaking, with a concrete example and common pitfalls to avoid.
21 Apr 2026, 01:42 UTC

Why choose standalone components
Standalone components let you declare a component, its directives and pipes directly in the class file with the standalone: true flag. This removes the need for an NgModule wrapper, which can simplify the file structure and enable better tree‑shaking during builds.
Worked example: creating and bootstrapping a standalone component
- Generate a new component with the standalone flag:
- Bootstrap the component in
main.tsusing the standalone bootstrap API: - Lazy‑load a route that uses the same standalone component:
- Missing imports: If the component template uses *ngFor or *ngIf, you must include
CommonModule(orFormsModulefor ngModel) in theimportsarray. Forgetting this results in a runtime error like "Cannot read property 'forEach' of undefined". - Provider conflicts: Standalone components cannot directly consume providers that are only declared inside an NgModule unless those providers are re‑exported from the module and imported into the standalone component’s
providersarray. Otherwise you may get unexpected null injections. - Circular dependencies: Because each standalone file is treated as a separate module graph node, importing two standalone components that depend on each other creates a circular dependency that can break tree‑shaking. Keep the dependency graph acyclic or refactor shared logic into a third standalone file.
- Version requirement: Standalone components were introduced in Angular 14. Projects using earlier versions must upgrade before using the
standalone: trueflag. - Run the generation command and inspect the generated file:
- Build the application for production and examine the bundle:
ng generate component hero-card --standalone
The command creates hero-card.component.ts containing:
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-hero-card',
standalone: true,
imports: [CommonModule],
template: `<div>{{ hero.name }}</div>`
})
export class HeroCardComponent {
hero = { name: 'Windstorm' };
}
import { bootstrapApplication } from '@angular/platform-browser';
import { HeroCardComponent } from './app/hero-card/hero-card.component';
import { provideRouter } from '@angular/router';
import { routes } from './app/routes';
bootstrapApplication(HeroCardComponent, {
providers: [provideRouter(routes)]
});
const routes = [
{
path: 'hero',
loadComponent: () =>
import('./hero-card/hero-card.component').then(m => m.HeroCardComponent)
}
];
When the application bundles, the Angular compiler treats HeroCardComponent as a self‑contained unit. No NgModule metadata is emitted, which allows the bundler to drop unused code more aggressively.
Limits and common mistakes
Verification steps
ng generate component demo --standalone
cat src/app/demo/demo.component.ts
Confirm the metadata includes standalone: true and an imports array.
ng build --configuration production
source-map-explorer dist/*/main*.js
Look for the absence of NgModule wrapper code around the component’s class definition and verify that the component’s code appears in the bundle only when the route that uses it is loaded.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.