Angular Standalone Components Explained — Modern Angular Without NgModules
In this tutorial, you will learn about Angular Standalone Components Explained. We cover key concepts, practical examples, and best practices to help you master this topic.
Angular standalone components are self-contained components that declare their own dependencies, removing the need for NgModules and simplifying the Angular application architecture.
What You'll Learn
- What standalone components are and why they existed
- How to create standalone components, directives, and pipes
- How to Bootstrap a standalone application
- How to configure providers and routing without NgModules
- How to migrate existing NgModule-based apps
Why It Matters
Standalone components simplify Angular significantly. You no longer need app.module.ts, shared.module.ts, or any NgModule files. This reduces boilerplate, makes code easier to understand, and lowers the barrier for beginners.
Real-World Use
Durga Antivirus Pro's dashboard was migrated from NgModule-based to standalone components. The Migration removed 40+ module files, cut build time by 15%, and made the codebase easier for new developers to navigate.
flowchart TD
A[Angular 14-] --> B[NgModules Required]
C[Angular 15+] --> D[Standalone Optional]
E[Angular 17+] --> F[Standalone Default]
B --> G[Module file per feature]
D --> H[No module files]
D --> I[imports in component]
style E fill:#f97316,color:#fff
Creating a Standalone Component
Set standalone: true in the component decorator:
import { Component } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";
@Component({
selector: "app-todo-input",
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<input [(ngModel)]="newTask" placeholder="Add a new task" />
<button (click)="addTask()">Add</button>
`
})
export class TodoInputComponent {
newTask = "";
addTask() {
if (this.newTask.trim()) {
console.log("Adding task:", this.newTask);
this.newTask = "";
}
}
}
Expected output: A text input and button. Typing and clicking logs the task to the console.
The imports array replaces the NgModule. You import exactly what the component needs — no more, no less.
Bootstrapping a Standalone App
Replace NgModule bootstrap with bootstrapApplication:
// main.ts
import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app/app.component";
bootstrapApplication(AppComponent).catch(err => console.error(err));
Expected output: The app boots without any NgModule file. AppComponent must be standalone.
For routing and providers:
import { bootstrapApplication } from "@angular/platform-browser";
import { provideRouter } from "@angular/router";
import { provideHttpClient } from "@angular/common/http";
import { AppComponent } from "./app/app.component";
import { routes } from "./app/app.routes";
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes),
provideHttpClient(),
]
}).catch(err => console.error(err));
Expected output: The app boots with routing and HTTP client configured, all without a single NgModule.
provideRouter and provideHttpClient are provider functions that return the necessary configuration. This pattern replaces RouterModule.forRoot(routes) and HttpClientModule.
Standalone Directives and Pipes
Directives and pipes can also be standalone:
import { Directive, ElementRef, HostListener, Input } from "@angular/core";
@Directive({
selector: "[appHighlight]",
standalone: true
})
export class HighlightDirective {
@Input() appHighlight = "";
constructor(private el: ElementRef) {}
@HostListener("mouseenter") onEnter() {
this.el.nativeElement.style.backgroundColor = this.appHighlight || "yellow";
}
@HostListener("mouseleave") onLeave() {
this.el.nativeElement.style.backgroundColor = "transparent";
}
}
Import it in any standalone component:
@Component({
selector: "app-text-display",
standalone: true,
imports: [HighlightDirective],
template: `<p [appHighlight]="'lightblue'">Hover to highlight</p>`
})
export class TextDisplayComponent {}
Expected output: Hovering over the text highlights it in light blue.
Directives and pipes use the same standalone: true flag. Import them wherever they are needed without creating a shared module.
Lazy Loading with Standalone
Lazy loading works naturally with standalone components:
import { Routes } from "@angular/router";
import { HomeComponent } from "./home/home.component";
export const routes: Routes = [
{ path: "", component: HomeComponent },
{
path: "dashboard",
loadComponent: () => import("./dashboard/dashboard.component").then(m => m.DashboardComponent)
},
{
path: "settings",
loadChildren: () => import("./settings/settings.routes").then(m => m.settingsRoutes)
}
];
Expected output: The DashboardComponent is only loaded when the user navigates to /dashboard.
Since the DashboardComponent is standalone and declares its own dependencies, Angular knows exactly what to include in the lazy-loaded chunk. No NgModule is needed.
Standalone vs NgModule Comparison
Before and after migration:
// OLD: NgModule approach
// app.module.ts
@NgModule({
declarations: [AppComponent, UserListComponent],
imports: [BrowserModule, FormsModule, HttpClientModule, RouterModule.forRoot(routes)],
providers: [UserService],
bootstrap: [AppComponent]
})
export class AppModule {}
// NEW: Standalone approach
// main.ts just bootstraps AppComponent
// AppComponent imports everything it needs directly
@Component({
selector: "app-root",
standalone: true,
imports: [CommonModule, FormsModule, RouterOutlet, RouterLink],
template: `<router-outlet></router-outlet>`
})
export class AppComponent {}
Expected output: The standalone version eliminates the entire app.module.ts file and moves imports to where they are used.
The declarations array is gone. Components no longer need to be declared. They import their dependencies directly.
Common Mistakes
Importing NgModule into standalone components — Do not import
NgModule-based modules likeBrowserModuleinto standalone components. UseCommonModuleinstead.Forgetting standalone flag — Components without
standalone: truestill need an NgModule. The error "Cannot determine the module for this component" indicates this.Mixing module and standalone declarations — A standalone component cannot be declared in an NgModule's
declarationsarray. Useimportsinstead.Missing provideRouter — Without
provideRouter, therouter-outletandrouterLinkdirectives work but routing does not function.Lazy loading NgModule-based components —
loadComponentonly works with standalone components. For NgModule-based components, useloadChildren.
Practice Questions
What does
standalone: truemean? The component manages its own dependencies via theimportsarray and does not belong to any NgModule.How do you bootstrap a standalone app? Use
bootstrapApplication(AppComponent, { providers: [...] })instead ofplatformBrowserDynamic().bootstrapModule(AppModule).How does lazy loading work with standalone components? Use
loadComponent: () => import(...)in the route configuration. Angular lazily loads only the component and its declared dependencies.Can standalone components use NgModule-based libraries? Yes, import the NgModule in the component's
importsarray. Angular handles the interop.What replaces RouterModule.forRoot() in standalone apps?
provideRouter(routes)in theprovidersarray ofbootstrapApplication.
Challenge
Migrate a small NgModule-based app (AppModule with 3 components, routing, HTTP client) to standalone. Remove all NgModule files and use bootstrapApplication with provider functions. The final app should have zero .module.ts files.
FAQ
Mini Project
Build a fully standalone RecipeManagerApp. Use bootstrapApplication with provideRouter and provideHttpClient. Create standalone components for recipe list, recipe detail, and add-recipe form. Use lazy loading for the detail view. Do not create any NgModule files. The entire app should consist of component files, a routes file, and main.ts.
What's Next
Continue with modern Angular features:
Angular Signals, Angular Content Projection, Angular Standalone
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro