Skip to content

Angular Standalone Components Explained — Modern Angular Without NgModules

DodaTech Updated 2026-06-28 5 min read

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

  1. Importing NgModule into standalone components — Do not import NgModule-based modules like BrowserModule into standalone components. Use CommonModule instead.

  2. Forgetting standalone flag — Components without standalone: true still need an NgModule. The error "Cannot determine the module for this component" indicates this.

  3. Mixing module and standalone declarations — A standalone component cannot be declared in an NgModule's declarations array. Use imports instead.

  4. Missing provideRouter — Without provideRouter, the router-outlet and routerLink directives work but routing does not function.

  5. Lazy loading NgModule-based componentsloadComponent only works with standalone components. For NgModule-based components, use loadChildren.

Practice Questions

  1. What does standalone: true mean? The component manages its own dependencies via the imports array and does not belong to any NgModule.

  2. How do you bootstrap a standalone app? Use bootstrapApplication(AppComponent, { providers: [...] }) instead of platformBrowserDynamic().bootstrapModule(AppModule).

  3. 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.

  4. Can standalone components use NgModule-based libraries? Yes, import the NgModule in the component's imports array. Angular handles the interop.

  5. What replaces RouterModule.forRoot() in standalone apps? provideRouter(routes) in the providers array of bootstrapApplication.

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

Are standalone components the default now?

Yes, since Angular 17, the CLI generates standalone components by default. NgModules are optional.

Can I use NgModules and standalone together?

Yes, Angular supports mixing both approaches during migration. A standalone component can import an NgModule-based library.

What happens to shared modules?

Import individual components, directives, and pipes directly instead of wrapping them in a shared NgModule.

Do I still need AppModule?

No. bootstrapApplication replaces NgModule.bootstrap. The app boots from the root component.

Will NgModules be removed from Angular?

NgModules remain supported but are no longer required. The Angular team recommends standalone for new projects.

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