Angular 21 makes standalone components the default. If you've worked with NgModules before, standalone feels like removing scaffolding that was never really necessary — your component just declares what it needs, directly.

What Changed from NgModules

With NgModules, every component needed to be declared in a module before it could be used anywhere:

// Old NgModule approach
@NgModule({
  declarations: [NavbarComponent, UserCardComponent],
  imports: [CommonModule, RouterModule],
  exports: [NavbarComponent]
})
export class SharedModule {}

With standalone components, the component itself is the unit of reuse — no module needed:

// Angular 21 standalone approach
@Component({
  selector: 'app-navbar',
  standalone: true,
  imports: [RouterLink, NgIf, NgClass],
  templateUrl: './navbar.component.html'
})
export class NavbarComponent {}

Your First Standalone Component

// src/app/components/user-card/user-card.component.ts
import { Component, Input } from '@angular/core'
import { NgIf, NgClass } from '@angular/common'

@Component({
  selector: 'app-user-card',
  standalone: true,
  imports: [NgIf, NgClass],
  template: `
    <div class="card border-0 shadow-sm" [ngClass]="{ 'border-danger': isAdmin }">
      <div class="card-body d-flex align-items-center gap-3 p-4">
        <div class="rounded-circle text-white fw-bold d-flex align-items-center justify-content-center"
          style="width:48px;height:48px;background:#fd4766;font-size:0.85rem;flex-shrink:0;">
          {{ initials }}
        </div>
        <div>
          <h6 class="fw-bold mb-0">{{ name }}</h6>
          <p class="text-muted small mb-0">{{ email }}</p>
          <span *ngIf="isAdmin" class="badge text-white mt-1" style="background:#fd4766;">Admin</span>
        </div>
      </div>
    </div>
  `
})
export class UserCardComponent {
  @Input() name = ''
  @Input() email = ''
  @Input() isAdmin = false

  get initials(): string {
    return this.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2)
  }
}

Using it in another standalone component — just add it to imports:

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [UserCardComponent, NgFor],
  template: `
    <div class="container py-4">
      <h1 class="fw-bold mb-4">Team</h1>
      <div class="row g-3">
        <div class="col-md-6" *ngFor="let user of users">
          <app-user-card
            [name]="user.name"
            [email]="user.email"
            [isAdmin]="user.isAdmin"
          />
        </div>
      </div>
    </div>
  `
})
export class DashboardComponent {
  users = [
    { name: 'Gagan Singh', email: 'gagan@lettstartdesign.com', isAdmin: true },
    { name: 'Rahul Kumar', email: 'rahul@company.com', isAdmin: false },
    { name: 'Priya Sharma', email: 'priya@company.com', isAdmin: false },
  ]
}

Bootstrapping Without AppModule

main.ts looks like this in Angular 21:

// src/main.ts
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))

No AppModule, no BrowserModule declaration. Providers like router and HTTP client are registered directly.

Common Imports Reference

Instead of importing CommonModule for everything, import only what you need:

DirectiveImport from
*ngIf / @ifNgIf from @angular/common
*ngFor / @forNgFor from @angular/common
[ngClass]NgClass from @angular/common
[ngStyle]NgStyle from @angular/common
routerLinkRouterLink from @angular/router
routerLinkActiveRouterLinkActive from @angular/router
<router-outlet>RouterOutlet from @angular/router
async pipeAsyncPipe from @angular/common
All of the aboveCommonModule from @angular/common

Importing individually is better for tree-shaking — your final bundle only includes directives the component actually uses.

New Control Flow Syntax (Angular 17+)

Angular 21 prefers the @if / @for / @switch block syntax over structural directives. These don't need any imports at all:

@Component({
  selector: 'app-user-list',
  standalone: true,
  // No NgIf or NgFor needed — @if and @for are built-in
  template: `
    @if (users.length > 0) {
      <div class="list-group">
        @for (user of users; track user.id) {
          <div class="list-group-item d-flex justify-content-between">
            <span>{{ user.name }}</span>
            @if (user.isAdmin) {
              <span class="badge text-white" style="background:#fd4766;">Admin</span>
            }
          </div>
        }
      </div>
    } @else {
      <p class="text-muted">No users found.</p>
    }
  `
})
export class UserListComponent {
  users: { id: number; name: string; isAdmin: boolean }[] = []
}

Migrating an Existing NgModule Component

If you're moving a project from NgModules to standalone:

// Before — declared in a module
@Component({
  selector: 'app-sidebar',
  templateUrl: './sidebar.component.html'
})
export class SidebarComponent {}

// In some-module.ts
@NgModule({
  declarations: [SidebarComponent],
  imports: [CommonModule, RouterModule],
  exports: [SidebarComponent]
})
export class SomeModule {}
// After — fully standalone
@Component({
  selector: 'app-sidebar',
  standalone: true,
  imports: [NgFor, NgIf, RouterLink, RouterLinkActive],
  templateUrl: './sidebar.component.html'
})
export class SidebarComponent {}

Steps for each component:

  1. Add standalone: true to @Component
  2. Add any directives/pipes/components it uses to imports: []
  3. Remove it from the declarations array of whichever module had it
  4. Remove it from exports in that module too
  5. Any component that uses this one now imports it directly

Angular CLI has a schematic that can do this automatically: ng generate @angular/core:standalone.

Frequently Asked Questions

A standalone component is an Angular component that manages its own imports directly in its @Component decorator, without needing to be declared in an NgModule. The standalone: true flag (default in Angular 19+) tells the compiler this component is self-sufficient.
No. NgModules are now optional in Angular 21. The Angular team recommends standalone components for all new projects. NgModules still work for backward compatibility but you won't need them for anything you build from scratch.
Import CommonModule directly in your component's imports array: @Component({ standalone: true, imports: [CommonModule] }). Or import specific directives individually: imports: [NgIf, NgFor, NgClass] — tree-shaking works better with individual imports.
In main.ts, use bootstrapApplication(AppComponent, { providers: [...] }) instead of platformBrowserDynamic().bootstrapModule(AppModule). Pass providers for routing, HTTP, and other services directly in the config object.

Need a Full Bootstrap 5 Admin Dashboard?

Get a complete Angular 21 + Bootstrap 5 dashboard with 50+ components — built by the same team behind BootstrapPlanet.

Browse Templates →

Use code FIRST30 for 30% off your first purchase.

Related Components