Understand the difference between NgModules and standalone components, and learn when to use each approach in Angular applications.
Understand the difference between NgModules and standalone components, and learn when to use each approach in Angular applications.
Learn about Traditional NgModule Architecture.
1import { NgModule } from '@angular/core';
2import { CommonModule } from '@angular/common';
3import { FormsModule } from '@angular/forms';
4import { RouterModule } from '@angular/router';
5
6import { UserListComponent } from './user-list.component';
7import { UserCardComponent } from './user-card.component';
8import { UserService } from './user.service';
9
10@NgModule({
11 declarations: [
12 UserListComponent,
13 UserCardComponent
14 ],
15 imports: [
16 CommonModule,
17 FormsModule,
18 RouterModule.forChild([
19 { path: '', component: UserListComponent }
20 ])
21 ],
22 providers: [UserService],
23 exports: [UserListComponent]
24})
25export class UserModule { }
26
27// App Module
28import { NgModule } from '@angular/core';
29import { BrowserModule } from '@angular/platform-browser';
30import { HttpClientModule } from '@angular/common/http';
31
32import { AppComponent } from './app.component';
33import { UserModule } from './user/user.module';
34
35@NgModule({
36 declarations: [AppComponent],
37 imports: [
38 BrowserModule,
39 HttpClientModule,
40 UserModule
41 ],
42 providers: [],
43 bootstrap: [AppComponent]
44})
45export class AppModule { }declarations:
Components, directives, pipes owned by the module
Other modules this module depends on
providers:
Services available to the module
Components/directives/pipes available to importing modules
bootstrap:
Root component (only in AppModule)
Learn about Standalone Components (Angular 14+).
1import { Component } from '@angular/core';
2import { CommonModule } from '@angular/common';
3import { FormsModule } from '@angular/forms';
4
5@Component({
6 selector: 'app-user-card',
7 standalone: true,
8 imports: [CommonModule, FormsModule],
9 template: \`
10 <div class="user-card">
11 <h3>{{ user.name }}</h3>
12 <p>{{ user.email }}</p>
13 </div>
14 \`
15})
16export class UserCardComponent {
17 @Input() user!: { name: string; email: string };
18}
19
20@Component({
21 selector: 'app-user-list',
22 standalone: true,
23 imports: [CommonModule, UserCardComponent],
24 template: \`
25 <div>
26 @for (user of users; track user.id) {
27 <app-user-card [user]="user"></app-user-card>
28 }
29 </div>
30 \`
31})
32export class UserListComponent {
33 users = [
34 { id: 1, name: 'John', email: '[email protected]' },
35 { id: 2, name: 'Jane', email: '[email protected]' }
36 ];
37}Learn about Bootstrapping Standalone Applications.
1// main.ts - Standalone bootstrap
2import { bootstrapApplication } from '@angular/platform-browser';
3import { provideRouter } from '@angular/router';
4import { provideHttpClient } from '@angular/common/http';
5import { AppComponent } from './app/app.component';
6import { routes } from './app/app.routes';
7
8bootstrapApplication(AppComponent, {
9 providers: [
10 provideRouter(routes),
11 provideHttpClient(),
12 provideAnimations(),
13 // Add other providers here
14 ]
15}).catch(err => console.error(err));
16
17// app.component.ts
18import { Component } from '@angular/core';
19import { RouterOutlet } from '@angular/router';
20import { HeaderComponent } from './header/header.component';
21
22@Component({
23 selector: 'app-root',
24 standalone: true,
25 imports: [RouterOutlet, HeaderComponent],
26 template: \`
27 <app-header></app-header>
28 <router-outlet></router-outlet>
29 \`
30})
31export class AppComponent { }Learn about Routing with Standalone Components.
1// app.routes.ts
2import { Routes } from '@angular/router';
3
4export const routes: Routes = [
5 {
6 path: '',
7 redirectTo: 'home',
8 pathMatch: 'full'
9 },
10 {
11 path: 'home',
12 loadComponent: () => import('./home/home.component')
13 .then(m => m.HomeComponent)
14 },
15 {
16 path: 'users',
17 loadComponent: () => import('./users/user-list.component')
18 .then(m => m.UserListComponent)
19 },
20 {
21 path: 'users/:id',
22 loadComponent: () => import('./users/user-detail.component')
23 .then(m => m.UserDetailComponent)
24 },
25 {
26 // Lazy load child routes
27 path: 'admin',
28 loadChildren: () => import('./admin/admin.routes')
29 .then(m => m.ADMIN_ROUTES)
30 }
31];
32
33// admin/admin.routes.ts
34import { Routes } from '@angular/router';
35import { AdminDashboardComponent } from './dashboard.component';
36import { AdminUsersComponent } from './users.component';
37
38export const ADMIN_ROUTES: Routes = [
39 {
40 path: '',
41 component: AdminDashboardComponent
42 },
43 {
44 path: 'users',
45 component: AdminUsersComponent
46 }
47];Learn about Dependency Injection in Standalone.
1import { Injectable, inject, InjectionToken } from '@angular/core';
2import { HttpClient } from '@angular/common/http';
3
4// Service with providedIn
5@Injectable({
6 providedIn: 'root'
7})
8export class UserService {
9 private http = inject(HttpClient);
10
11 getUsers() {
12 return this.http.get('/api/users');
13 }
14}
15
16// Component-level provider
17@Component({
18 selector: 'app-user-list',
19 standalone: true,
20 providers: [UserService],
21 template: \`<div>Users</div>\`
22})
23export class UserListComponent {
24 private userService = inject(UserService);
25}
26
27// Injection tokens
28export const API_URL = new InjectionToken<string>('API_URL');
29
30// Provide in bootstrapApplication
31bootstrapApplication(AppComponent, {
32 providers: [
33 { provide: API_URL, useValue: 'https://api.example.com' }
34 ]
35});
36
37// Inject the token
38@Injectable({ providedIn: 'root' })
39export class ApiService {
40 private apiUrl = inject(API_URL);
41}Learn about Migration Strategies.
1// Step 1: Convert leaf components (no dependencies)
2// Before (NgModule)
3@Component({
4 selector: 'app-button',
5 template: '<button><ng-content></ng-content></button>'
6})
7export class ButtonComponent { }
8
9// After (Standalone)
10@Component({
11 selector: 'app-button',
12 standalone: true,
13 template: '<button><ng-content></ng-content></button>'
14})
15export class ButtonComponent { }
16
17// Step 2: Update module to import standalone components
18@NgModule({
19 declarations: [UserListComponent],
20 imports: [
21 CommonModule,
22 ButtonComponent // Import standalone component
23 ]
24})
25export class UserModule { }
26
27// Step 3: Convert component with dependencies
28// Before
29@Component({
30 selector: 'app-user-card',
31 template: \`
32 <div>
33 <app-button>View</app-button>
34 </div>
35 \`
36})
37export class UserCardComponent { }
38
39// After
40@Component({
41 selector: 'app-user-card',
42 standalone: true,
43 imports: [ButtonComponent],
44 template: \`
45 <div>
46 <app-button>View</app-button>
47 </div>
48 \`
49})
50export class UserCardComponent { }
51
52// Step 4: Use Angular CLI schematic
53// ng generate @angular/core:standalone
54// This converts components/directives/pipes to standaloneStandalone
Boilerplate
More verbose
Minimal boilerplate
Dependencies
Module-level imports
Component-level imports
Lazy Loading
loadChildren with modules
loadComponent directly
Tree Shaking
Less effective
More effective
TestBed with modules
Direct component imports
Encapsulation
Module boundaries
Component boundaries
Legacy approach
Recommended approach
Use standalone components for new Angular projects
Migrate existing apps incrementally from leaves to roots
Leverage lazy loading with loadComponent for better performance
Use providedIn: 'root' for application-wide services
Import only what you need in each component
Use provideRouter, provideHttpClient in bootstrapApplication
Organize routes in separate .routes.ts files
Keep module-based code during migration for stability
Use Angular CLI schematics for automated migration
Standalone components eliminate the need for NgModules in most cases
NgModules organize related components, while standalone makes each component self-contained
Standalone components improve tree-shaking and reduce bundle sizes
bootstrapApplication replaces platformBrowserDynamic().bootstrapModule()
Lazy loading is simpler with loadComponent instead of loadChildren modules
Migration from NgModules to standalone is incremental and non-breaking
Component-level imports make dependencies explicit and easier to understand
Providers can be scoped to components or provided globally via bootstrapApplication
Standalone is the recommended approach for all new Angular applications
Your progress is saved in this browser, on this device only.